Bladeren bron

Changed ConnectionLease to be taken as &mut, updated tests.

Kestrel 3 weken geleden
bovenliggende
commit
aa9c4fc7e4

+ 32 - 62
microrm/src/db.rs

@@ -135,12 +135,12 @@ impl ConnectionData {
 }
 
 impl<'l> ConnectionLease<'l> {
-    pub(crate) fn execute_raw_sql(&self, sql: impl AsRef<str>) -> DBResult<()> {
+    pub(crate) fn execute_raw_sql(&mut self, sql: impl AsRef<str>) -> DBResult<()> {
         self.conn.execute_raw_sql(sql)
     }
 
     pub(crate) fn with_prepared<R>(
-        &self,
+        &mut self,
         hash_key: impl PreparedKey,
         build_query: impl Fn() -> String,
         run_query: impl Fn(StatementContext) -> DBResult<R>,
@@ -207,47 +207,6 @@ impl<'l> AsRef<ConnectionLease<'l>> for ConnectionLease<'l> {
     }
 }
 
-/// Trait for objects that can provide a [`ConnectionLease`]
-pub trait LeaseContainer {
-    fn lease(&mut self) -> &mut ConnectionLease<'_>;
-}
-
-/// Provides access to a ConnectionLease.
-pub trait ConnectionLeaser {
-    /// Accessor function
-    fn lease<'s>(&'s self) -> impl AsRef<ConnectionLease<'s>>;
-}
-
-impl<'l> ConnectionLeaser for ConnectionLease<'l> {
-    fn lease<'s>(&'s self) -> impl AsRef<ConnectionLease<'s>> {
-        struct Access<'a, 'b: 'a>(&'a ConnectionLease<'b>);
-        impl<'a, 'b: 'a> AsRef<ConnectionLease<'a>> for Access<'a, 'b> {
-            fn as_ref(&self) -> &'a ConnectionLease<'b> {
-                self.0
-            }
-        }
-
-        Access(self)
-    }
-}
-
-impl ConnectionLeaser for ConnectionData {
-    fn lease<'s>(&'s self) -> impl AsRef<ConnectionLease<'s>> {
-        struct Access<'d>(ConnectionLease<'d>);
-        impl<'d> AsRef<ConnectionLease<'d>> for Access<'d> {
-            fn as_ref(&self) -> &ConnectionLease<'d> {
-                &self.0
-            }
-        }
-
-        Access(ConnectionLease {
-            pool: Weak::new(),
-            conn: self,
-            index: 0,
-        })
-    }
-}
-
 impl<'l> Drop for ConnectionLease<'l> {
     fn drop(&mut self) {
         if let Some(pooldata) = self.pool.upgrade() {
@@ -334,12 +293,21 @@ impl<'l> From<&'l str> for ConnectionPoolConfig<'l> {
     }
 }
 
-impl ConnectionLeaser for ConnectionPool {
-    fn lease<'s>(&'s self) -> impl AsRef<ConnectionLease<'s>> {
-        self.acquire().expect("implicit lease acquisition failure")
+impl<'l> From<&'l String> for ConnectionPoolConfig<'l> {
+    fn from(value: &'l String) -> Self {
+        Self {
+            uri: value.as_str(),
+            pool_size: Self::DEFAULT_POOL_SIZE,
+        }
     }
 }
 
+/*impl ConnectionLeaser for ConnectionPool {
+    fn lease<'s>(&'s self) -> impl AsMut<ConnectionLease<'s>> {
+        self.acquire().expect("implicit lease acquisition failure")
+    }
+}*/
+
 #[derive(Clone)]
 /// Multithreading-safe database connection pool.
 pub struct ConnectionPool {
@@ -375,13 +343,13 @@ impl ConnectionPool {
 unsafe impl Send for ConnectionPool {}
 unsafe impl Sync for ConnectionPool {}
 
-pub(crate) struct Transaction<'l> {
-    db: &'l ConnectionLease<'l>,
+pub(crate) struct Transaction<'l, 'd: 'l> {
+    db: &'l mut ConnectionLease<'d>,
     committed: bool,
 }
 
-impl<'l> Transaction<'l> {
-    pub fn new(db: &'l ConnectionLease) -> DBResult<Self> {
+impl<'l, 'd: 'l> Transaction<'l, 'd> {
+    pub fn new(db: &'l mut ConnectionLease<'d>) -> DBResult<Self> {
         db.execute_raw_sql("BEGIN TRANSACTION")?;
         Ok(Self {
             db,
@@ -389,6 +357,10 @@ impl<'l> Transaction<'l> {
         })
     }
 
+    pub fn lease(&mut self) -> &mut ConnectionLease<'d> {
+        self.db
+    }
+
     pub fn commit(mut self) -> DBResult<()> {
         self.committed = true;
 
@@ -396,7 +368,7 @@ impl<'l> Transaction<'l> {
     }
 }
 
-impl<'l> Drop for Transaction<'l> {
+impl<'l, 'd: 'l> Drop for Transaction<'l, 'd> {
     fn drop(&mut self) {
         if !self.committed {
             let _ = self.db.execute_raw_sql("ROLLBACK");
@@ -404,11 +376,11 @@ impl<'l> Drop for Transaction<'l> {
     }
 }
 
-impl<'l> ConnectionLeaser for Transaction<'l> {
+/*impl<'l> ConnectionLeaser for Transaction<'l> {
     fn lease<'s>(&'s self) -> impl AsRef<ConnectionLease<'s>> {
         self.db
     }
-}
+}*/
 
 struct Statement {
     #[allow(unused)]
@@ -441,26 +413,24 @@ impl Drop for Statement {
 
 #[cfg(test)]
 mod test {
-    use super::{ConnectionLeaser, ConnectionPool};
+    use super::ConnectionPool;
 
     #[test]
     fn simple_sql() {
         let c = ConnectionPool::new(":memory:").expect("couldn't open test db");
-        let l = c.lease();
-        l.as_ref()
-            .execute_raw_sql("CREATE TABLE test_table (id integer primary key, value string)")
+        let mut l = c.acquire().unwrap();
+        l.execute_raw_sql("CREATE TABLE test_table (id integer primary key, value string)")
             .expect("couldn't execute sql");
     }
 
     #[test]
     fn prepare_stmt() {
         let c = ConnectionPool::new(":memory:").expect("couldn't open test db");
-        let c = c.lease();
-        let c = c.as_ref();
-        c.execute_raw_sql("CREATE TABLE test_table (id integer primary key, value string)")
+        let mut cl = c.acquire().unwrap();
+        cl.execute_raw_sql("CREATE TABLE test_table (id integer primary key, value string)")
             .expect("couldn't execute sql");
 
-        c.with_prepared(
+        cl.with_prepared(
             1,
             || format!("INSERT INTO test_table VALUES (?, ?)"),
             |ctx| {
@@ -474,7 +444,7 @@ mod test {
         )
         .expect("couldn't run prepared INSERT statement");
 
-        c.with_prepared(
+        cl.with_prepared(
             2,
             || format!("SELECT * FROM test_table"),
             |ctx| {

+ 13 - 13
microrm/src/glue.rs

@@ -1,7 +1,7 @@
 // query implementations on schema types
 
 use crate::{
-    db::{ConnectionLease, ConnectionLeaser, StatementContext, Transaction},
+    db::{ConnectionLease, StatementContext, Transaction},
     query::{self, Insertable, Query, QueryPart, Queryable, RelationInterface},
     schema::{
         entity::Entity,
@@ -17,9 +17,9 @@ use crate::{
 
 impl<T: Entity> Stored<T> {
     /// Synchronize the wrapped value with the corresponding database row.
-    pub fn sync(&mut self, lease: &ConnectionLease) -> DBResult<()> {
-        let txn = Transaction::new(lease)?;
-        query::base_queries::update_entity(lease, self)?;
+    pub fn sync(&mut self, lease: &mut ConnectionLease) -> DBResult<()> {
+        let mut txn = Transaction::new(lease)?;
+        query::base_queries::update_entity(txn.lease(), self)?;
         txn.commit()
     }
 }
@@ -30,7 +30,7 @@ impl<T: Entity> Stored<T> {
 
 impl<T: Entity> IDMap<T> {
     /// Look up an Entity in this map by ID.
-    pub fn by_id(&self, lease: &ConnectionLease, id: T::ID) -> DBResult<Option<Stored<T>>> {
+    pub fn by_id(&self, lease: &mut ConnectionLease, id: T::ID) -> DBResult<Option<Stored<T>>> {
         self.with(T::IDPart::default(), &id).first().get(lease)
     }
 }
@@ -52,27 +52,27 @@ impl<'a, T: Entity> Queryable for &'a IDMap<T> {
 }
 
 impl<T: Entity> Insertable<T> for IDMap<T> {
-    fn insert(&self, lease: &ConnectionLease, value: T) -> DBResult<T::ID> {
-        let txn = Transaction::new(lease)?;
-        let out = query::base_queries::insert(txn.lease().as_ref(), &value)?;
+    fn insert(&self, lease: &mut ConnectionLease, value: T) -> DBResult<T::ID> {
+        let mut txn = Transaction::new(lease)?;
+        let out = query::base_queries::insert(txn.lease(), &value)?;
         txn.commit()?;
         Ok(out)
     }
 
-    fn insert_and_return(&self, lease: &ConnectionLease, value: T) -> DBResult<Stored<T>> {
-        let txn = Transaction::new(lease)?;
-        let out = query::base_queries::insert_and_return(txn.lease().as_ref(), value)?;
+    fn insert_and_return(&self, lease: &mut ConnectionLease, value: T) -> DBResult<Stored<T>> {
+        let mut txn = Transaction::new(lease)?;
+        let out = query::base_queries::insert_and_return(txn.lease(), value)?;
         txn.commit()?;
         Ok(out)
     }
 }
 
 impl<'l, T: Entity> Insertable<T> for &'l IDMap<T> {
-    fn insert(&self, lease: &ConnectionLease, value: T) -> DBResult<T::ID> {
+    fn insert(&self, lease: &mut ConnectionLease, value: T) -> DBResult<T::ID> {
         <IDMap<T> as Insertable<T>>::insert(self, lease, value)
     }
 
-    fn insert_and_return(&self, lease: &ConnectionLease, value: T) -> DBResult<Stored<T>> {
+    fn insert_and_return(&self, lease: &mut ConnectionLease, value: T) -> DBResult<Stored<T>> {
         <IDMap<T> as Insertable<T>>::insert_and_return(self, lease, value)
     }
 }

+ 27 - 19
microrm/src/lib.rs

@@ -36,38 +36,42 @@
 //!     value: String,
 //! }
 //!
-//! #[derive(Schema)]
-//! struct KVDB {
+//! #[derive(Default, Schema)]
+//! struct KVSchema {
 //!     kvs: microrm::IDMap<KVEntry>,
 //! }
 //!
 //! # fn main() -> Result<(), microrm::Error> {
-//! let db = KVDB::open_path(":memory:")?;
-//! db.kvs.insert(KVEntry {
+//! let cpool = microrm::ConnectionPool::new(":memory:")?;
+//! let mut lease = cpool.acquire()?;
+//! let schema = KVSchema::default();
+//! schema.install(&mut lease)?;
+//! // let db = KVSchema::open_path(":memory:")?;
+//! schema.kvs.insert(&mut lease, KVEntry {
 //!     key: "example_key".to_string(),
 //!     value: "example_value".to_string()
 //! })?;
 //!
 //! // can get with a String reference
 //! assert_eq!(
-//!     db.kvs.keyed(&String::from("example_key")).get()?.map(|v| v.value.clone()),
+//!     schema.kvs.keyed(&String::from("example_key")).get(&mut lease)?.map(|v| v.value.clone()),
 //!     Some("example_value".to_string()));
 //! // thanks to the QueryEquivalent trait, we can also just use a plain &str
 //! assert_eq!(
-//!     db.kvs.keyed("example_key").get()?.map(|v| v.value.clone()),
+//!     schema.kvs.keyed("example_key").get(&mut lease)?.map(|v| v.value.clone()),
 //!     Some("example_value".to_string()));
 //!
 //! // obviously, if we get another KV entry with a missing key, it doesn't come back...
-//! assert_eq!(db.kvs.keyed("another_example_key").get()?.is_some(), false);
+//! assert_eq!(schema.kvs.keyed("another_example_key").get(&mut lease)?.is_some(), false);
 //!
 //! // note that the above all return an Option<Stored<T>>. when using filters on arbitrary
 //! // columns, a Vec<Stored<T>> is returned:
 //! assert_eq!(
-//!     db
+//!     schema
 //!         .kvs
 //!         // note that the column constant uses CamelCase
 //!         .with(KVEntry::Value, "example_value")
-//!         .get()?
+//!         .get(&mut lease)?
 //!         .into_iter()
 //!         .map(|v| v.wrapped().value).collect::<Vec<_>>(),
 //!     vec!["example_value".to_string()]);
@@ -150,7 +154,7 @@
 //!     pub contents: microrm::RelationMap<Product>,
 //! }
 //!
-//! #[derive(Database)]
+//! #[derive(Default, Schema)]
 //! pub struct ECommerceDB {
 //!     pub products: microrm::IDMap<Product>,
 //!     pub customers: microrm::IDMap<Customer>,
@@ -158,10 +162,14 @@
 //! }
 //! # fn main() -> Result<(), microrm::Error> {
 //! // open a database instance
-//! let db = ECommerceDB::open_path(":memory:")?;
+//! let cpool = microrm::ConnectionPool::new(":memory:")?;
+//! let mut lease = cpool.acquire()?;
+//! let schema = ECommerceDB::default();
+//! // let db = ECommerceDB::open_path(":memory:")?;
+//! schema.install(&mut lease);
 //!
 //! // add an example product
-//! let widget1 = db.products.insert_and_return(Product {
+//! let widget1 = schema.products.insert_and_return(&mut lease, Product {
 //!     title: "Widget Title Here".into(),
 //!     longform_body: "The first kind of widget that WidgetCo produces.".into(),
 //!     cost: 100.98,
@@ -169,13 +177,13 @@
 //! })?;
 //!
 //! // add an image for the product
-//! widget1.images.insert(ProductImage {
+//! widget1.images.insert(&mut lease, ProductImage {
 //!     product: widget1.id(),
 //!     img_data: [/* image data goes here */].into(),
 //! });
 //!
 //! // sign us up for this most excellent ecommerce website
-//! let customer1 = db.customers.insert_and_return(Customer {
+//! let customer1 = schema.customers.insert_and_return(&mut lease, Customer {
 //!     email: "your@email.here".into(),
 //!     legal_name: "Douglas Adams".into(),
 //!     password_salt: "pepper".into(),
@@ -185,18 +193,18 @@
 //! })?;
 //!
 //! // put in an order for the widget!
-//! let mut order1 = db.orders.insert_and_return(Order {
+//! let mut order1 = schema.orders.insert_and_return(&mut lease, Order {
 //!     order_state: vec![OrderState::AwaitingPayment].into(),
 //!     customer: Default::default(),
 //!     shipping_address: "North Pole, Canada, H0H0H0".into(),
 //!     billing_address: None,
 //!     contents: Default::default(),
 //! })?;
-//! order1.contents.connect_to(widget1.id())?;
-//! order1.customer.connect_to(customer1.id())?;
+//! order1.contents.connect_to(&mut lease, widget1.id())?;
+//! order1.customer.connect_to(&mut lease, customer1.id())?;
 //!
 //! // Now get all products that customer1 has ever ordered
-//! let all_ordered = customer1.orders.join(Order::Contents).get()?;
+//! let all_ordered = customer1.orders.join(Order::Contents).get(&mut lease)?;
 //! assert_eq!(all_ordered, vec![widget1]);
 //!
 //! // process the payment for our order by updating the entity
@@ -207,7 +215,7 @@
 //! );
 //!
 //! // now synchronize the entity changes back into the database
-//! order1.sync()?;
+//! order1.sync(&mut lease)?;
 //!
 //! # Ok(())
 //! # }

+ 32 - 32
microrm/src/query.rs

@@ -1,7 +1,7 @@
 use itertools::Itertools;
 
 use crate::{
-    db::{ConnectionLease, ConnectionLeaser, StatementContext, Transaction},
+    db::{ConnectionLease, StatementContext, Transaction},
     schema::{
         datum::{Datum, QueryEquivalent, QueryEquivalentList},
         entity::{Entity, EntityID, EntityPart, EntityPartList},
@@ -212,7 +212,7 @@ pub trait RelationInterface {
     /// Attempt to connect the contextual instance to a remote instance.
     fn connect_to(
         &self,
-        lease: &ConnectionLease,
+        lease: &mut ConnectionLease,
         remote_id: <Self::RemoteEntity as Entity>::ID,
     ) -> DBResult<()>
     where
@@ -221,9 +221,9 @@ pub trait RelationInterface {
         let rdata = self.get_data()?;
         let an = RelationNames::collect::<Self>(self)?;
 
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
 
-        base_queries::do_connect::<Self::RemoteEntity>(lease, rdata, an, remote_id)?;
+        base_queries::do_connect::<Self::RemoteEntity>(txn.lease(), rdata, an, remote_id)?;
 
         txn.commit()
     }
@@ -231,7 +231,7 @@ pub trait RelationInterface {
     /// Attempt to disconnect the contextual instance from a remote instance.
     fn disconnect_from(
         &self,
-        lease: &ConnectionLease,
+        lease: &mut ConnectionLease,
         remote_id: <Self::RemoteEntity as Entity>::ID,
     ) -> DBResult<()>
     where
@@ -240,10 +240,10 @@ pub trait RelationInterface {
         let rdata = self.get_data()?;
         let an = RelationNames::collect::<Self>(self)?;
 
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
 
         // second, add to the relation table
-        txn.lease().as_ref().with_prepared(
+        txn.lease().with_prepared(
             hash_of(("disconnect", an.local_name, an.remote_name, an.part_name)),
             || {
                 format!(
@@ -272,16 +272,16 @@ pub trait RelationInterface {
 /// Represents a context in which we can insert an entity type `E`.
 pub trait Insertable<E: Entity> {
     /// Insert an entity instance and return its new ID.
-    fn insert(&self, lease: &ConnectionLease, value: E) -> DBResult<E::ID>;
+    fn insert(&self, lease: &mut ConnectionLease, value: E) -> DBResult<E::ID>;
     /// Insert an entity instance and return a [`Stored`] instance that can be used to synchronize
     /// its values back into the database later.
-    fn insert_and_return(&self, lease: &ConnectionLease, value: E) -> DBResult<Stored<E>>;
+    fn insert_and_return(&self, lease: &mut ConnectionLease, value: E) -> DBResult<Stored<E>>;
 }
 
 impl<AI: RelationInterface> Insertable<AI::RemoteEntity> for AI {
     fn insert(
         &self,
-        lease: &ConnectionLease,
+        lease: &mut ConnectionLease,
         value: AI::RemoteEntity,
     ) -> DBResult<<AI::RemoteEntity as Entity>::ID>
     where
@@ -294,12 +294,12 @@ impl<AI: RelationInterface> Insertable<AI::RemoteEntity> for AI {
         let rdata = self.get_data()?;
         let an = RelationNames::collect::<Self>(self)?;
 
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
 
         // so first, into the remote table
-        let remote_id = base_queries::insert(lease, &value)?;
+        let remote_id = base_queries::insert(txn.lease(), &value)?;
         // then the relation
-        base_queries::do_connect::<AI::RemoteEntity>(lease, rdata, an, remote_id)?;
+        base_queries::do_connect::<AI::RemoteEntity>(txn.lease(), rdata, an, remote_id)?;
 
         txn.commit()?;
 
@@ -308,7 +308,7 @@ impl<AI: RelationInterface> Insertable<AI::RemoteEntity> for AI {
 
     fn insert_and_return(
         &self,
-        lease: &ConnectionLease,
+        lease: &mut ConnectionLease,
         value: AI::RemoteEntity,
     ) -> DBResult<Stored<AI::RemoteEntity>>
     where
@@ -321,12 +321,12 @@ impl<AI: RelationInterface> Insertable<AI::RemoteEntity> for AI {
         let rdata = self.get_data()?;
         let an = RelationNames::collect::<Self>(self)?;
 
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
 
         // so first, into the remote table
-        let remote = base_queries::insert_and_return(lease, value)?;
+        let remote = base_queries::insert_and_return(txn.lease(), value)?;
         // then the relation
-        base_queries::do_connect::<AI::RemoteEntity>(lease, rdata, an, remote.id())?;
+        base_queries::do_connect::<AI::RemoteEntity>(txn.lease(), rdata, an, remote.id())?;
 
         txn.commit()?;
 
@@ -359,13 +359,13 @@ pub trait Queryable: Clone {
     /// Count all entities in the current context.
     ///
     /// Returns the number of entities.
-    fn count(self, lease: &ConnectionLease) -> DBResult<usize>
+    fn count(self, lease: &mut ConnectionLease) -> DBResult<usize>
     where
         Self: Sized,
     {
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
         struct CountTag;
-        let out = txn.lease().as_ref().with_prepared(
+        let out = txn.lease().with_prepared(
             std::any::TypeId::of::<(Self::StaticVersion, CountTag)>(),
             || {
                 self.build()
@@ -393,13 +393,13 @@ pub trait Queryable: Clone {
         Ok(out)
     }
     /// Get all entities in the current context.
-    fn get(self, lease: &ConnectionLease) -> DBResult<Self::OutputContainer>
+    fn get(self, lease: &mut ConnectionLease) -> DBResult<Self::OutputContainer>
     where
         Self: Sized,
     {
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
         struct GetTag;
-        let out = txn.lease().as_ref().with_prepared(
+        let out = txn.lease().with_prepared(
             std::any::TypeId::of::<(Self::StaticVersion, GetTag)>(),
             || self.build().assemble(),
             |mut ctx| {
@@ -416,14 +416,14 @@ pub trait Queryable: Clone {
     /// Get IDs of all entities in the current context.
     fn get_ids(
         self,
-        lease: &ConnectionLease,
+        lease: &mut ConnectionLease,
     ) -> DBResult<<Self::OutputContainer as OutputContainer<Self::EntityOutput>>::IDContainer>
     where
         Self: Sized,
     {
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
         struct GetIDTag;
-        let out = txn.lease().as_ref().with_prepared(
+        let out = txn.lease().with_prepared(
             std::any::TypeId::of::<(Self::StaticVersion, GetIDTag)>(),
             || {
                 self.build()
@@ -447,13 +447,13 @@ pub trait Queryable: Clone {
         Ok(out)
     }
     /// Delete all entities in the current context.
-    fn delete(self, lease: &ConnectionLease) -> DBResult<()>
+    fn delete(self, lease: &mut ConnectionLease) -> DBResult<()>
     where
         Self: Sized,
     {
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
         struct DeleteTag;
-        txn.lease().as_ref().with_prepared(
+        txn.lease().with_prepared(
             std::any::TypeId::of::<(Self::StaticVersion, DeleteTag)>(),
             || {
                 format!(
@@ -480,13 +480,13 @@ pub trait Queryable: Clone {
     }
 
     /// Delete all entities in the current context and return them
-    fn remove(self, lease: &ConnectionLease) -> DBResult<Self::OutputContainer>
+    fn remove(self, lease: &mut ConnectionLease) -> DBResult<Self::OutputContainer>
     where
         Self: Sized,
     {
-        let txn = Transaction::new(lease)?;
+        let mut txn = Transaction::new(lease)?;
         struct DeleteTag;
-        let out = txn.lease().as_ref().with_prepared(
+        let out = txn.lease().with_prepared(
             std::any::TypeId::of::<(Self::StaticVersion, DeleteTag)>(),
             || {
                 format!(

+ 7 - 4
microrm/src/query/base_queries.rs

@@ -11,7 +11,7 @@ use crate::{
 
 use super::RelationNames;
 
-pub(crate) fn insert<E: Entity>(conn: &ConnectionLease, value: &E) -> DBResult<E::ID> {
+pub(crate) fn insert<E: Entity>(conn: &mut ConnectionLease, value: &E) -> DBResult<E::ID> {
     struct InsertQuery<E: Entity>(std::marker::PhantomData<E>);
 
     conn.with_prepared(
@@ -86,7 +86,7 @@ pub(crate) fn insert<E: Entity>(conn: &ConnectionLease, value: &E) -> DBResult<E
 }
 
 pub(crate) fn insert_and_return<E: Entity>(
-    lease: &ConnectionLease,
+    lease: &mut ConnectionLease,
     mut value: E,
 ) -> DBResult<Stored<E>> {
     let id = insert(lease, &value)?;
@@ -109,7 +109,10 @@ pub(crate) fn insert_and_return<E: Entity>(
     Ok(Stored::new(id, value))
 }
 
-pub(crate) fn update_entity<E: Entity>(conn: &ConnectionLease, value: &Stored<E>) -> DBResult<()> {
+pub(crate) fn update_entity<E: Entity>(
+    conn: &mut ConnectionLease,
+    value: &Stored<E>,
+) -> DBResult<()> {
     struct UpdateQuery<E: Entity>(std::marker::PhantomData<E>);
 
     conn.with_prepared(
@@ -179,7 +182,7 @@ pub(crate) fn update_entity<E: Entity>(conn: &ConnectionLease, value: &Stored<E>
 }
 
 pub(crate) fn do_connect<Remote: Entity>(
-    lease: &ConnectionLease,
+    lease: &mut ConnectionLease,
     rdata: &RelationData,
     an: RelationNames,
     remote_id: Remote::ID,

+ 21 - 35
microrm/src/schema.rs

@@ -258,6 +258,14 @@ pub struct IDMap<T: Entity> {
     _ghost: std::marker::PhantomData<T>,
 }
 
+impl<T: Entity> Default for IDMap<T> {
+    fn default() -> Self {
+        Self {
+            _ghost: std::marker::PhantomData,
+        }
+    }
+}
+
 impl<T: Entity> Clone for IDMap<T> {
     fn clone(&self) -> Self {
         Self {
@@ -281,13 +289,13 @@ impl<E: Entity> DatabaseItem for IDMap<E> {
     }
 }
 
-/// Represents a single root-level table or index in a database.
+/// Represents a logical item in a database schema, be it an index, table, or logical grouping.
 pub trait DatabaseItem {
     /// Accept an entity visitor for entity discovery.
     fn accept_item_visitor(visitor: &mut impl DatabaseItemVisitor);
 }
 
-/// Visitor trait for iterating across the types in a [`Database`].
+/// Visitor trait for iterating across the types in a [`Schema`] tree.
 pub trait DatabaseItemVisitor {
     /// Visit an `IDMap<T>` type.
     fn visit_idmap<T: Entity>(&mut self)
@@ -300,40 +308,9 @@ pub trait DatabaseItemVisitor {
 }
 
 /// A root structure for the database specification graph.
-pub trait Schema {
-    /*
-    /// Open the SQLite database at the given URI and return it as an instance of a schema.
-    ///
-    /// This function will attempt to create the database file if it is not present.
-    fn open_path<U: AsRef<str>>(uri: U) -> DBResult<Self>
-    where
-        Self: Sized,
-    {
-        let pool = ConnectionPool::new(uri.as_ref())?;
-        Self::wrap_pool(pool)
-    }
-
-    fn wrap_pool(pool: ConnectionPool) -> DBResult<Self>
-    where
-        Self: Sized,
-    {
-        let schema = build::collect_from_database::<Self>();
-        match schema.check(&pool) {
-            // schema checks out
-            Some(true) => {},
-            // schema doesn't match
-            Some(false) => Err(Error::IncompatibleSchema)?,
-            // no schema found
-            None => {
-                schema.create(&pool)?;
-            },
-        }
-
-        Ok(Self::build(pool))
-    }*/
-
+pub trait Schema: Default {
     ///
-    fn install_schema(lease: &ConnectionLease) -> DBResult<()>
+    fn install(&self, lease: &mut ConnectionLease) -> DBResult<()>
     where
         Self: Sized,
     {
@@ -361,3 +338,12 @@ pub trait Schema {
     where
         Self: Sized;
 }
+
+impl<S: Schema> DatabaseItem for S {
+    fn accept_item_visitor(visitor: &mut impl DatabaseItemVisitor)
+    where
+        Self: Sized,
+    {
+        <S as Schema>::accept_item_visitor(visitor);
+    }
+}

+ 2 - 2
microrm/src/schema/build.rs

@@ -104,7 +104,7 @@ impl DatabaseSchema {
     /// - yes, this is a schema match (true)
     /// - no, this is not a schema match (false)
     /// - there is no schema that we know of (None)
-    pub fn check(&self, lease: &ConnectionLease) -> Option<bool> {
+    pub fn check(&self, lease: &mut ConnectionLease) -> Option<bool> {
         // attempt to use connection as a MetadataDB database
         let metadb = meta::MetadataDB::build();
 
@@ -118,7 +118,7 @@ impl DatabaseSchema {
             .map(|kv| kv.value.parse::<u64>().unwrap_or(0) == self.signature)
     }
 
-    pub fn create(&self, lease: &ConnectionLease) -> DBResult<()> {
+    pub fn create(&self, lease: &mut ConnectionLease) -> DBResult<()> {
         lease.execute_raw_sql("BEGIN TRANSACTION")?;
         for query in self.queries.iter() {
             log::trace!("Running creation query {query}");

+ 8 - 0
microrm/src/schema/index.rs

@@ -23,6 +23,14 @@ pub struct Index<E: Entity, EPL: EntityPartList<Entity = E>> {
     _ghost: std::marker::PhantomData<(E, EPL)>,
 }
 
+impl<E: Entity, EPL: EntityPartList<Entity = E>> Default for Index<E, EPL> {
+    fn default() -> Self {
+        Self {
+            _ghost: std::marker::PhantomData,
+        }
+    }
+}
+
 impl<E: Entity, EPL: EntityPartList<Entity = E>> Index<E, EPL> {
     /// Construct an Index instance.
     pub fn build() -> Self {

+ 1 - 1
microrm/src/schema/meta.rs

@@ -9,7 +9,7 @@ pub struct MicrormMeta {
     pub value: String,
 }
 
-#[derive(microrm_macros::Schema)]
+#[derive(Default, microrm_macros::Schema)]
 pub struct MetadataDB {
     pub metastore: IDMap<MicrormMeta>,
 }

+ 8 - 3
microrm/tests/common/mod.rs

@@ -1,14 +1,19 @@
 use microrm::prelude::*;
 
-pub fn open_test_db_helper<DB: Schema>(identifier: &'static str) -> DB {
+pub fn open_test_db_helper<DB: Schema>(identifier: &'static str) -> (microrm::ConnectionPool, DB) {
     let path = format!(
         "{tmpdir}/{identifier}.db",
         tmpdir = std::env!("CARGO_TARGET_TMPDIR"),
     );
     let path = path.replace("::", "_");
     let _ = std::fs::remove_file(path.as_str());
-    let pool = microrm::ConnectionPool::new(path).expect("couldn't connect to database");
-    DB::open_path(path).expect("couldn't open database file")
+    let pool = microrm::ConnectionPool::new(path.as_str()).expect("couldn't connect to database");
+    let db = DB::default();
+
+    db.install(&mut pool.acquire().expect("couldn't acquire lease"))
+        .expect("couldn't install schema");
+
+    (pool, db)
 }
 
 mod macros {

+ 10 - 9
microrm/tests/conflicts.rs

@@ -7,7 +7,7 @@ mod common;
 #[derive(Entity)]
 struct Meta {}
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct MetaConflictDB {
     #[allow(unused)]
     pub meta_list: microrm::IDMap<Meta>,
@@ -15,7 +15,7 @@ struct MetaConflictDB {
 
 #[test]
 fn builtin_name_conflict() {
-    let _db: MetaConflictDB = common::open_test_db!();
+    let (_pool, _db): (_, MetaConflictDB) = common::open_test_db!();
 }
 
 #[derive(Entity)]
@@ -23,7 +23,7 @@ struct OrdinaryEntity {
     v: usize,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct ConflictingDB {
     pub map1: microrm::IDMap<OrdinaryEntity>,
     pub map2: microrm::IDMap<OrdinaryEntity>,
@@ -32,13 +32,14 @@ struct ConflictingDB {
 #[test]
 #[should_panic(expected = "duplicate IDMap for entity ordinary_entity")]
 fn multimap_conflict() {
-    let db: ConflictingDB = common::open_test_db!();
+    let (pool, db): (_, ConflictingDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
-    assert_eq!(db.map1.count().unwrap(), 0);
-    assert_eq!(db.map2.count().unwrap(), 0);
+    assert_eq!(db.map1.count(&mut lease).unwrap(), 0);
+    assert_eq!(db.map2.count(&mut lease).unwrap(), 0);
 
-    db.map1.insert(OrdinaryEntity { v: 0 }).unwrap();
+    db.map1.insert(&mut lease, OrdinaryEntity { v: 0 }).unwrap();
 
-    assert_eq!(db.map1.count().unwrap(), 1);
-    assert_eq!(db.map2.count().unwrap(), 0);
+    assert_eq!(db.map1.count(&mut lease).unwrap(), 1);
+    assert_eq!(db.map2.count(&mut lease).unwrap(), 0);
 }

+ 107 - 51
microrm/tests/derive.rs

@@ -1,7 +1,7 @@
 #![allow(unused)]
 
 use microrm::prelude::*;
-use microrm::schema::SchemaIDMap;
+use microrm::schema::{IDMap, Schema};
 use test_log::test;
 
 mod common;
@@ -19,121 +19,169 @@ struct Person {
     roles: microrm::RelationMap<Role>,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct PeopleDB {
     people: IDMap<Person>,
 }
 
 #[test]
 fn open_test() {
-    PeopleDB::open_path(":memory:");
+    let (_pool, _db): (_, PeopleDB) = common::open_test_db!();
 }
 
 #[test]
 fn check_for_inserted() {
-    let db: PeopleDB = common::open_test_db!();
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
+
     let name_string = "name_here".to_string();
     // check that it isn't in the database before we insert it
-    assert!(db.people.keyed(&name_string).get().ok().flatten().is_none());
+    assert!(db
+        .people
+        .keyed(&name_string)
+        .get(&mut lease)
+        .ok()
+        .flatten()
+        .is_none());
 
     db.people
-        .insert(Person {
-            name: name_string.clone(),
-            roles: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Person {
+                name: name_string.clone(),
+                roles: Default::default(),
+            },
+        )
         .expect("failed to insert");
 
     // check that it is in the database after we insert it
-    assert!(db.people.keyed(&name_string).get().ok().flatten().is_some());
+    assert!(db
+        .people
+        .keyed(&name_string)
+        .get(&mut lease)
+        .ok()
+        .flatten()
+        .is_some());
 }
 
 #[test]
 fn check_relation_query_construction() {
-    let db: PeopleDB = common::open_test_db!();
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let name_string = "name_here".to_string();
     db.people
-        .insert(Person {
-            name: name_string.clone(),
-            roles: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Person {
+                name: name_string.clone(),
+                roles: Default::default(),
+            },
+        )
         .expect("couldn't insert test person");
 
     let person = db
         .people
         .keyed(&name_string)
-        .get()
+        .get(&mut lease)
         .ok()
         .flatten()
         .expect("couldn't re-get test person entity");
 
     person
         .roles
-        .get()
+        .get(&mut lease)
         .expect("couldn't get related role entity");
 }
 
 #[test]
 fn check_relation_insertion() {
-    let db: PeopleDB = common::open_test_db!();
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let name_string = "name_here".to_string();
     db.people
-        .insert(Person {
-            name: name_string.clone(),
-            roles: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Person {
+                name: name_string.clone(),
+                roles: Default::default(),
+            },
+        )
         .expect("couldn't insert test person");
 
     let person = db
         .people
         .keyed(&name_string)
-        .get()
+        .get(&mut lease)
         .ok()
         .flatten()
         .expect("couldn't re-get test person entity");
 
-    person.roles.insert(Role {
-        title: "title A".to_string(),
-        permissions: "permissions A".to_string(),
-    });
+    person.roles.insert(
+        &mut lease,
+        Role {
+            title: "title A".to_string(),
+            permissions: "permissions A".to_string(),
+        },
+    );
 }
 
 #[test]
 fn delete_test() {
-    let db: PeopleDB = common::open_test_db!();
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let id = db
         .people
-        .insert(Person {
-            name: "person_name".to_string(),
-            roles: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Person {
+                name: "person_name".to_string(),
+                roles: Default::default(),
+            },
+        )
         .expect("couldn't insert test person");
-    assert!(db.people.by_id(id).expect("couldn't query db").is_some());
+    assert!(db
+        .people
+        .by_id(&mut lease, id)
+        .expect("couldn't query db")
+        .is_some());
 
     db.people
         .with(
             <Person as microrm::schema::entity::Entity>::IDPart::default(),
             &id,
         )
-        .delete();
+        .delete(&mut lease);
 
-    assert!(db.people.by_id(id).expect("couldn't query db").is_none());
+    assert!(db
+        .people
+        .by_id(&mut lease, id)
+        .expect("couldn't query db")
+        .is_none());
 }
 
 #[test]
 fn remove_test() {
-    let db: PeopleDB = common::open_test_db!();
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let id = db
         .people
-        .insert(Person {
-            name: "person_name".to_string(),
-            roles: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Person {
+                name: "person_name".to_string(),
+                roles: Default::default(),
+            },
+        )
         .expect("couldn't insert test person");
-    assert!(db.people.by_id(id).expect("couldn't query db").is_some());
+    assert!(db
+        .people
+        .by_id(&mut lease, id)
+        .expect("couldn't query db")
+        .is_some());
 
     let removed = db
         .people
@@ -141,41 +189,49 @@ fn remove_test() {
             <Person as microrm::schema::entity::Entity>::IDPart::default(),
             &id,
         )
-        .remove()
+        .remove(&mut lease)
         .expect("couldn't execute removal query");
 
     assert_eq!(removed.len(), 1);
 
-    assert!(db.people.by_id(id).expect("couldn't query db").is_none());
+    assert!(db
+        .people
+        .by_id(&mut lease, id)
+        .expect("couldn't query db")
+        .is_none());
 }
 
 #[test]
 fn update_test() {
-    let db: PeopleDB = common::open_test_db!();
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let mut stored = db
         .people
-        .insert_and_return(Person {
-            name: "person_name".to_string(),
-            roles: Default::default(),
-        })
+        .insert_and_return(
+            &mut lease,
+            Person {
+                name: "person_name".to_string(),
+                roles: Default::default(),
+            },
+        )
         .expect("couldn't insert test person");
 
     assert_eq!(
         db.people
             .with(Person::Name, "person_name")
-            .count()
+            .count(&mut lease)
             .expect("couldn't execute count query"),
         1
     );
 
     stored.name = "another_person_name".into();
-    stored.sync();
+    stored.sync(&mut lease);
 
     assert_eq!(
         db.people
             .with(Person::Name, "person_name")
-            .count()
+            .count(&mut lease)
             .expect("couldn't execute count query"),
         0
     );

+ 18 - 11
microrm/tests/fkey.rs

@@ -15,7 +15,7 @@ struct Parent {
     name: String,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct PeopleDB {
     children: microrm::IDMap<Child>,
     parents: microrm::IDMap<Parent>,
@@ -23,26 +23,33 @@ struct PeopleDB {
 
 #[test]
 fn simple_query() {
-    let db: PeopleDB = common::open_test_db!(); // ::<PeopleDB>("fkey_simple_query");
+    let (pool, db): (_, PeopleDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let pid = db
         .parents
-        .insert(Parent {
-            name: "parent1".to_string(),
-        })
+        .insert(
+            &mut lease,
+            Parent {
+                name: "parent1".to_string(),
+            },
+        )
         .unwrap();
     let _c1id = db
         .children
-        .insert(Child {
-            name: "child1".to_string(),
-            parent: pid,
-        })
+        .insert(
+            &mut lease,
+            Child {
+                name: "child1".to_string(),
+                parent: pid,
+            },
+        )
         .unwrap();
 
     assert_eq!(
         db.children
             .foreign(Child::Parent)
-            .get_ids()
+            .get_ids(&mut lease)
             .expect("couldn't run query"),
         vec![pid]
     );
@@ -51,7 +58,7 @@ fn simple_query() {
         db.children
             .keyed("child1")
             .foreign(Child::Parent)
-            .get_ids()
+            .get_ids(&mut lease)
             .expect("couldn't run query"),
         Some(pid)
     );

+ 26 - 19
microrm/tests/index.rs

@@ -13,7 +13,7 @@ struct TwoWay {
     right: String,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct TWDB {
     entries: microrm::IDMap<TwoWay>,
     left_index: Index<TwoWay, index_cols![TwoWay::left]>,
@@ -22,40 +22,47 @@ struct TWDB {
 
 #[test]
 fn db_construction() {
-    let _db: TWDB = common::open_test_db!();
+    let (_, _db): (_, TWDB) = common::open_test_db!();
 }
 
 #[test]
 fn lookup() {
-    let db: TWDB = common::open_test_db!();
+    let (pool, db): (_, TWDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let e1 = db
         .entries
-        .insert_and_return(TwoWay {
-            up: "up1".into(),
-            left: "left1".into(),
-            right: "right1".into(),
-        })
+        .insert_and_return(
+            &mut lease,
+            TwoWay {
+                up: "up1".into(),
+                left: "left1".into(),
+                right: "right1".into(),
+            },
+        )
         .expect("couldn't insert entry");
     let e2 = db
         .entries
-        .insert_and_return(TwoWay {
-            up: "up2".into(),
-            left: "left2".into(),
-            right: "right2".into(),
-        })
+        .insert_and_return(
+            &mut lease,
+            TwoWay {
+                up: "up2".into(),
+                left: "left2".into(),
+                right: "right2".into(),
+            },
+        )
         .expect("couldn't insert entry");
 
     assert!(db
         .entries
         .indexed(&db.left_index, "left3")
-        .get()
+        .get(&mut lease)
         .expect("couldn't query left index")
         .is_none());
     assert_eq!(
         db.entries
             .indexed(&db.left_index, "left2")
-            .get()
+            .get(&mut lease)
             .expect("couldn't query left index")
             .as_ref(),
         Some(&e2)
@@ -63,7 +70,7 @@ fn lookup() {
     assert_eq!(
         db.entries
             .indexed(&db.left_index, "left1")
-            .get()
+            .get(&mut lease)
             .expect("couldn't query left index")
             .as_ref(),
         Some(&e1)
@@ -72,13 +79,13 @@ fn lookup() {
     assert!(db
         .entries
         .indexed(&db.right_index, "right3")
-        .get()
+        .get(&mut lease)
         .expect("couldn't query right index")
         .is_none());
     assert_eq!(
         db.entries
             .indexed(&db.right_index, "right2")
-            .get()
+            .get(&mut lease)
             .expect("couldn't query right index")
             .as_ref(),
         Some(&e2)
@@ -86,7 +93,7 @@ fn lookup() {
     assert_eq!(
         db.entries
             .indexed(&db.right_index, "right1")
-            .get()
+            .get(&mut lease)
             .expect("couldn't query right index")
             .as_ref(),
         Some(&e1)

+ 35 - 22
microrm/tests/injective.rs

@@ -28,7 +28,7 @@ struct Book {
     creator: microrm::RelationRange<AuthorBookRelation>,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct ABDB {
     authors: microrm::IDMap<Author>,
     books: microrm::IDMap<Book>,
@@ -36,54 +36,67 @@ struct ABDB {
 
 #[test]
 fn schema_creation() {
-    let _db: ABDB = common::open_test_db!();
+    let (_pool, _db): (_, ABDB) = common::open_test_db!();
 }
 
 #[test]
 fn single_connection() {
-    let db: ABDB = common::open_test_db!();
+    let (pool, db): (_, ABDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let a1 = db
         .authors
-        .insert_and_return(Author {
-            name: "Homer".to_string(),
-            works: Default::default(),
-        })
+        .insert_and_return(
+            &mut lease,
+            Author {
+                name: "Homer".to_string(),
+                works: Default::default(),
+            },
+        )
         .expect("couldn't insert author");
 
     let a2 = db
         .authors
-        .insert_and_return(Author {
-            name: "Virgil".to_string(),
-            works: Default::default(),
-        })
+        .insert_and_return(
+            &mut lease,
+            Author {
+                name: "Virgil".to_string(),
+                works: Default::default(),
+            },
+        )
         .expect("couldn't insert author");
 
     let b1_id = db
         .books
-        .insert(Book {
-            title: "Odyssey".to_string(),
-            creator: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Book {
+                title: "Odyssey".to_string(),
+                creator: Default::default(),
+            },
+        )
         .expect("couldn't insert book");
     let b2_id = db
         .books
-        .insert(Book {
-            title: "Aeneid".to_string(),
-            creator: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Book {
+                title: "Aeneid".to_string(),
+                creator: Default::default(),
+            },
+        )
         .expect("couldn't insert book");
 
     a1.works
-        .connect_to(b1_id)
+        .connect_to(&mut lease, b1_id)
         .expect("couldn't connect a1 and b1");
     a2.works
-        .connect_to(b2_id)
+        .connect_to(&mut lease, b2_id)
         .expect("couldn't connect a2 and b2");
 
     // we can't claim that Homer wrote the Aeneid because it's an injective relationship and
     // in this model, only one Author can claim the Book as their work
-    match a1.works.connect_to(b2_id) {
+    match a1.works.connect_to(&mut lease, b2_id) {
         Err(microrm::Error::ConstraintViolation(_)) => {
             // all good
         },

+ 77 - 49
microrm/tests/join.rs

@@ -20,7 +20,7 @@ struct IndirectTarget {
     name: String,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct JoinDB {
     bases: microrm::IDMap<Base>,
     targets: microrm::IDMap<Target>,
@@ -29,113 +29,141 @@ struct JoinDB {
 
 #[test]
 fn simple_ops() {
-    let db: JoinDB = common::open_test_db!();
+    let (pool, db): (_, JoinDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let b1id = db
         .bases
-        .insert(Base {
-            name: "base1".to_string(),
-            targets: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Base {
+                name: "base1".to_string(),
+                targets: Default::default(),
+            },
+        )
         .expect("couldn't insert base");
     let b2id = db
         .bases
-        .insert(Base {
-            name: "base2".to_string(),
-            targets: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Base {
+                name: "base2".to_string(),
+                targets: Default::default(),
+            },
+        )
         .expect("couldn't insert base");
     let _b3id = db
         .bases
-        .insert(Base {
-            name: "base3".to_string(),
-            targets: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Base {
+                name: "base3".to_string(),
+                targets: Default::default(),
+            },
+        )
         .expect("couldn't insert base");
 
     let t1id = db
         .targets
-        .insert(Target {
-            name: "target1".to_string(),
-            ..Default::default()
-        })
+        .insert(
+            &mut lease,
+            Target {
+                name: "target1".to_string(),
+                ..Default::default()
+            },
+        )
         .expect("couldn't insert target");
     let t2id = db
         .targets
-        .insert(Target {
-            name: "target2".to_string(),
-            ..Default::default()
-        })
+        .insert(
+            &mut lease,
+            Target {
+                name: "target2".to_string(),
+                ..Default::default()
+            },
+        )
         .expect("couldn't insert target");
     let t3id = db
         .targets
-        .insert(Target {
-            name: "target3".to_string(),
-            ..Default::default()
-        })
+        .insert(
+            &mut lease,
+            Target {
+                name: "target3".to_string(),
+                ..Default::default()
+            },
+        )
         .expect("couldn't insert target");
 
     let it1id = db
         .indirect
-        .insert(IndirectTarget {
-            name: "itarget1".to_string(),
-            ..Default::default()
-        })
+        .insert(
+            &mut lease,
+            IndirectTarget {
+                name: "itarget1".to_string(),
+                ..Default::default()
+            },
+        )
         .expect("couldn't insert target");
     let _it2id = db
         .indirect
-        .insert(IndirectTarget {
-            name: "itarget2".to_string(),
-            ..Default::default()
-        })
+        .insert(
+            &mut lease,
+            IndirectTarget {
+                name: "itarget2".to_string(),
+                ..Default::default()
+            },
+        )
         .expect("couldn't insert target");
     let _it3id = db
         .indirect
-        .insert(IndirectTarget {
-            name: "itarget3".to_string(),
-            ..Default::default()
-        })
+        .insert(
+            &mut lease,
+            IndirectTarget {
+                name: "itarget3".to_string(),
+                ..Default::default()
+            },
+        )
         .expect("couldn't insert target");
 
     log::trace!("looking up base by ID {:?}", b1id);
 
     let b1 = db
         .bases
-        .by_id(b1id)
+        .by_id(&mut lease, b1id)
         .expect("couldn't get base")
         .expect("couldn't get base");
     b1.targets
-        .connect_to(t1id)
+        .connect_to(&mut lease, t1id)
         .expect("couldn't connect b1 to t1id");
     b1.targets
-        .connect_to(t2id)
+        .connect_to(&mut lease, t2id)
         .expect("couldn't connect b1 to t2id");
 
     let b2 = db
         .bases
-        .by_id(b2id)
+        .by_id(&mut lease, b2id)
         .expect("couldn't get base")
         .expect("couldn't get base");
     b2.targets
-        .connect_to(t2id)
+        .connect_to(&mut lease, t2id)
         .expect("couldn't connect b2 to t2id");
     b2.targets
-        .connect_to(t3id)
+        .connect_to(&mut lease, t3id)
         .expect("couldn't connect b2 to t3id");
 
     let t1 = db
         .targets
-        .by_id(t2id)
+        .by_id(&mut lease, t2id)
         .expect("couldn't get target")
         .expect("couldn't get target");
     t1.indirect_targets
-        .connect_to(it1id)
+        .connect_to(&mut lease, it1id)
         .expect("couldn't connect t1 to it1id");
 
     assert_eq!(
         db.bases
             .join(Base::Targets)
-            .get()
+            .get(&mut lease)
             .expect("couldn't get joined results")
             .len(),
         3
@@ -145,7 +173,7 @@ fn simple_ops() {
         .bases
         .join(Base::Targets)
         .join(Target::IndirectTargets)
-        .get()
+        .get(&mut lease)
         .expect("couldn't get double-joined results");
     assert_eq!(double_join.len(), 1);
 
@@ -153,7 +181,7 @@ fn simple_ops() {
         .bases
         .join(Base::Targets)
         .join(Target::IndirectTargets)
-        .count()
+        .count(&mut lease)
         .expect("couldn't count double-joined results");
     assert_eq!(double_join_count, 1);
 }

+ 2 - 1
microrm/tests/manual_construction.rs

@@ -4,7 +4,7 @@
 use microrm::db::{ConnectionPool, StatementContext, StatementRow};
 use microrm::schema::datum::{ConcreteDatum, Datum};
 use microrm::schema::entity::{Entity, EntityID, EntityPart, EntityPartVisitor, EntityVisitor};
-use microrm::schema::{DatabaseItemVisitor, IDMap, SchemaDatabaseItem};
+use microrm::schema::{DatabaseItem, DatabaseItemVisitor, IDMap, Schema};
 use test_log::test;
 
 #[derive(Debug)]
@@ -120,6 +120,7 @@ impl Entity for SimpleEntity {
     }
 }
 
+#[derive(Default)]
 struct SimpleDatabase {
     strings: IDMap<SimpleEntity>,
 }

+ 31 - 21
microrm/tests/mutual.rs

@@ -23,7 +23,7 @@ struct Receipt {
     customers: microrm::RelationRange<CR>,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct ReceiptDB {
     customers: microrm::IDMap<Customer>,
     receipts: microrm::IDMap<Receipt>,
@@ -31,40 +31,50 @@ struct ReceiptDB {
 
 #[test]
 fn check_schema_creation() {
-    let _db: ReceiptDB = common::open_test_db!();
+    let (_pool, _db): (_, ReceiptDB) = common::open_test_db!();
 }
 
 #[test]
 fn simple_operations() {
-    let db: ReceiptDB = common::open_test_db!();
+    let (pool, db): (_, ReceiptDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     let ca = db
         .customers
-        .insert(Customer {
-            name: "customer A".to_string(),
-            receipts: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Customer {
+                name: "customer A".to_string(),
+                receipts: Default::default(),
+            },
+        )
         .expect("couldn't insert customer record");
 
     let ra = db
         .receipts
-        .insert(Receipt {
-            value: 32usize,
-            customers: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Receipt {
+                value: 32usize,
+                customers: Default::default(),
+            },
+        )
         .expect("couldn't insert receipt record");
 
     let rb = db
         .receipts
-        .insert(Receipt {
-            value: 64usize,
-            customers: Default::default(),
-        })
+        .insert(
+            &mut lease,
+            Receipt {
+                value: 64usize,
+                customers: Default::default(),
+            },
+        )
         .expect("couldn't insert receipt record");
 
     let e_ca = db
         .customers
-        .by_id(ca)
+        .by_id(&mut lease, ca)
         .expect("couldn't retrieve customer record")
         .expect("no customer record");
 
@@ -74,7 +84,7 @@ fn simple_operations() {
         ra
     );
     e_ca.receipts
-        .connect_to(ra)
+        .connect_to(&mut lease, ra)
         .expect("couldn't relationiate customer with receipt a");
     log::trace!(
         "connecting customer a (ID {:?}) to receipt b (ID {:?})",
@@ -82,7 +92,7 @@ fn simple_operations() {
         rb
     );
     e_ca.receipts
-        .connect_to(rb)
+        .connect_to(&mut lease, rb)
         .expect("couldn't relationiate customer with receipt b");
 
     log::trace!("connected!");
@@ -91,7 +101,7 @@ fn simple_operations() {
     // valid behaviour
     assert_eq!(
         e_ca.receipts
-            .get()
+            .get(&mut lease)
             .expect("couldn't get receipts related with customer")
             .into_iter()
             .map(|x| x.id())
@@ -102,13 +112,13 @@ fn simple_operations() {
     // check that the reverse direction was also added
     let e_ra = db
         .receipts
-        .by_id(ra)
+        .by_id(&mut lease, ra)
         .expect("couldn't retreieve receipt record")
         .expect("no receipt record");
 
     assert_eq!(
         e_ra.customers
-            .get()
+            .get(&mut lease)
             .expect("couldn't get related customers")
             .into_iter()
             .map(|x| x.id())

+ 4 - 3
microrm/tests/query_construct.rs

@@ -10,18 +10,19 @@ struct KV {
     value: String,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct KVDB {
     entries: microrm::IDMap<KV>,
 }
 
 #[test]
 fn repeated_uniqueness() {
-    let db: KVDB = common::open_test_db!();
+    let (pool, db): (_, KVDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     db.entries
         .first()
         .first()
-        .get()
+        .get(&mut lease)
         .expect("couldn't execute query with multiple uniqueness constraints");
 }

+ 87 - 56
microrm/tests/query_equiv.rs

@@ -34,7 +34,7 @@ struct SerializedItem {
     e: ExampleEnum,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct ItemDB {
     single_items: microrm::IDMap<SingleItem>,
     double_items: microrm::IDMap<DoubleItem>,
@@ -43,119 +43,143 @@ struct ItemDB {
 
 #[test]
 fn single_item() {
-    let db: ItemDB = common::open_test_db!();
+    let (pool, db): (_, ItemDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     db.single_items
-        .insert(SingleItem {
-            s: "string 1".into(),
-            v: vec![0u8, 1u8, 2u8],
-        })
+        .insert(
+            &mut lease,
+            SingleItem {
+                s: "string 1".into(),
+                v: vec![0u8, 1u8, 2u8],
+            },
+        )
         .expect("couldn't insert test item");
 
     db.single_items
-        .insert(SingleItem {
-            s: "string 2".into(),
-            v: vec![0u8, 1u8, 2u8],
-        })
+        .insert(
+            &mut lease,
+            SingleItem {
+                s: "string 2".into(),
+                v: vec![0u8, 1u8, 2u8],
+            },
+        )
         .expect("couldn't insert test item");
 
     assert!(db
         .single_items
         .keyed("string 2")
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_some());
     assert!(db
         .single_items
         .keyed("string 3")
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_none());
 
     assert!(db
         .single_items
         .keyed(String::from("string 2"))
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_some());
     assert!(db
         .single_items
         .keyed(String::from("string 3"))
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_none());
 }
 
 #[test]
 fn double_item() {
-    let db: ItemDB = common::open_test_db!();
+    let (pool, db): (_, ItemDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     db.double_items
-        .insert(DoubleItem {
-            s: "string 1".into(),
-            t: "second string 1".into(),
-            v: 42,
-        })
+        .insert(
+            &mut lease,
+            DoubleItem {
+                s: "string 1".into(),
+                t: "second string 1".into(),
+                v: 42,
+            },
+        )
         .expect("couldn't insert test item");
 
     db.double_items
-        .insert(DoubleItem {
-            s: "string 2".into(),
-            t: "second string 2".into(),
-            v: 6,
-        })
+        .insert(
+            &mut lease,
+            DoubleItem {
+                s: "string 2".into(),
+                t: "second string 2".into(),
+                v: 6,
+            },
+        )
         .expect("couldn't insert test item");
 
     assert!(db
         .double_items
         .keyed(("string 2", "second string 2"))
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_some());
     assert!(db
         .double_items
         .keyed(("string 3", "second string 3"))
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_none());
 
     assert!(db
         .double_items
         .keyed((String::from("string 2"), String::from("second string 2")))
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_some());
     assert!(db
         .double_items
         .keyed((String::from("string 3"), String::from("second string 3")))
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_none());
 }
 
 #[test]
 fn serialized_test() {
-    let db: ItemDB = common::open_test_db!();
+    let (pool, db): (_, ItemDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     db.serialized_items
-        .insert(SerializedItem {
-            discrim: 0,
-            e: ExampleEnum::Value1.into(),
-        })
+        .insert(
+            &mut lease,
+            SerializedItem {
+                discrim: 0,
+                e: ExampleEnum::Value1.into(),
+            },
+        )
         .expect("couldn't insert test item");
 
     db.serialized_items
-        .insert(SerializedItem {
-            discrim: 1,
-            e: ExampleEnum::Value1.into(),
-        })
+        .insert(
+            &mut lease,
+            SerializedItem {
+                discrim: 1,
+                e: ExampleEnum::Value1.into(),
+            },
+        )
         .expect("couldn't insert test item");
 
     db.serialized_items
-        .insert(SerializedItem {
-            discrim: 1,
-            e: ExampleEnum::Value2.into(),
-        })
+        .insert(
+            &mut lease,
+            SerializedItem {
+                discrim: 1,
+                e: ExampleEnum::Value2.into(),
+            },
+        )
         .expect("couldn't insert test item");
 
     assert!(db
@@ -163,7 +187,7 @@ fn serialized_test() {
         .with(SerializedItem::E, ExampleEnum::Value1)
         .keyed((1, ExampleEnum::Value1))
         .first()
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_some());
     assert!(db
@@ -171,7 +195,7 @@ fn serialized_test() {
         .with(SerializedItem::E, ExampleEnum::Value1)
         .keyed((1024, ExampleEnum::Value1))
         .first()
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_none());
     assert!(db
@@ -179,33 +203,40 @@ fn serialized_test() {
         .with(SerializedItem::E, ExampleEnum::Value1)
         .keyed((0, ExampleEnum::Value2))
         .first()
-        .get()
+        .get(&mut lease)
         .expect("couldn't query db")
         .is_none());
 }
 
 #[test]
 fn with_test() {
-    let db: ItemDB = common::open_test_db!();
+    let (pool, db): (_, ItemDB) = common::open_test_db!();
+    let mut lease = pool.acquire().unwrap();
 
     db.single_items
-        .insert(SingleItem {
-            s: "string 1".into(),
-            v: vec![0u8, 1u8, 2u8],
-        })
+        .insert(
+            &mut lease,
+            SingleItem {
+                s: "string 1".into(),
+                v: vec![0u8, 1u8, 2u8],
+            },
+        )
         .expect("couldn't insert test item");
 
     db.single_items
-        .insert(SingleItem {
-            s: "string 2".into(),
-            v: vec![0u8, 1u8, 2u8],
-        })
+        .insert(
+            &mut lease,
+            SingleItem {
+                s: "string 2".into(),
+                v: vec![0u8, 1u8, 2u8],
+            },
+        )
         .expect("couldn't insert test item");
 
     assert_eq!(
         db.single_items
             .with(SingleItem::V, [0u8].as_slice())
-            .get()
+            .get(&mut lease)
             .expect("couldn't query db")
             .len(),
         0
@@ -213,7 +244,7 @@ fn with_test() {
     assert_eq!(
         db.single_items
             .with(SingleItem::V, [0u8, 1, 2].as_slice())
-            .get()
+            .get(&mut lease)
             .expect("couldn't query db")
             .len(),
         2

+ 4 - 2
microrm/tests/reserved.rs

@@ -1,5 +1,7 @@
 #![allow(unused)]
 
+mod common;
+
 use microrm::prelude::*;
 use test_log::test;
 
@@ -13,12 +15,12 @@ struct Group {
     by: microrm::RelationMap<Select>,
 }
 
-#[derive(Database)]
+#[derive(Default, Schema)]
 struct ReservedWordDB {
     group: microrm::IDMap<Group>,
 }
 
 #[test]
 fn open_test() {
-    ReservedWordDB::open_path(":memory:");
+    let (_pool, _db): (_, ReservedWordDB) = common::open_test_db!();
 }