WIP almost compiling with sqlez

Mikayla Maki created

Change summary

crates/db/src/kvp.rs                       |  6 +-
crates/db/src/workspace.rs                 | 65 +++++++++--------------
crates/sqlez/src/bindable.rs               | 22 ++++++++
crates/sqlez/src/connection.rs             | 14 ++++-
crates/sqlez/src/migrations.rs             |  6 +-
crates/sqlez/src/savepoint.rs              |  8 +-
crates/sqlez/src/statement.rs              | 11 +--
crates/sqlez/src/thread_safe_connection.rs |  2 
8 files changed, 77 insertions(+), 57 deletions(-)

Detailed changes

crates/db/src/kvp.rs 🔗

@@ -17,21 +17,21 @@ impl Db {
     pub fn read_kvp(&self, key: &str) -> Result<Option<String>> {
         self.0
             .prepare("SELECT value FROM kv_store WHERE key = (?)")?
-            .bind(key)?
+            .with_bindings(key)?
             .maybe_row()
     }
 
     pub fn write_kvp(&self, key: &str, value: &str) -> Result<()> {
         self.0
             .prepare("INSERT OR REPLACE INTO kv_store(key, value) VALUES (?, ?)")?
-            .bind((key, value))?
+            .with_bindings((key, value))?
             .exec()
     }
 
     pub fn delete_kvp(&self, key: &str) -> Result<()> {
         self.0
             .prepare("DELETE FROM kv_store WHERE key = (?)")?
-            .bind(key)?
+            .with_bindings(key)?
             .exec()
     }
 }

crates/db/src/workspace.rs 🔗

@@ -23,17 +23,17 @@ use super::Db;
 pub(crate) const WORKSPACES_MIGRATION: Migration = Migration::new(
     "workspace",
     &[indoc! {"
-            CREATE TABLE workspaces(
-                workspace_id INTEGER PRIMARY KEY,
-                timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL
-            ) STRICT;
-            
-            CREATE TABLE worktree_roots(
-                worktree_root BLOB NOT NULL,
-                workspace_id INTEGER NOT NULL,
-                FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
-                PRIMARY KEY(worktree_root, workspace_id)
-            ) STRICT;"}],
+        CREATE TABLE workspaces(
+            workspace_id INTEGER PRIMARY KEY,
+            timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL
+        ) STRICT;
+        
+        CREATE TABLE worktree_roots(
+            worktree_root BLOB NOT NULL,
+            workspace_id INTEGER NOT NULL,
+            FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
+            PRIMARY KEY(worktree_root, workspace_id)
+        ) STRICT;"}],
 );
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
@@ -159,9 +159,9 @@ impl Db {
 
     /// Returns the previous workspace ids sorted by last modified along with their opened worktree roots
     pub fn recent_workspaces(&self, limit: usize) -> Vec<(WorkspaceId, Vec<Arc<Path>>)> {
-        let res = self.with_savepoint("recent_workspaces", |conn| {
+        self.with_savepoint("recent_workspaces", |conn| {
             let ids = conn.prepare("SELECT workspace_id FROM workspaces ORDER BY last_opened_timestamp DESC LIMIT ?")?
-                .bind(limit)?
+                .with_bindings(limit)?
                 .rows::<i64>()?
                 .iter()
                 .map(|row| WorkspaceId(*row));
@@ -170,7 +170,7 @@ impl Db {
             
             let stmt = conn.prepare("SELECT worktree_root FROM worktree_roots WHERE workspace_id = ?")?;
             for workspace_id in ids {
-                let roots = stmt.bind(workspace_id.0)?
+                let roots = stmt.with_bindings(workspace_id.0)?
                     .rows::<Vec<u8>>()?
                     .iter()
                     .map(|row| {
@@ -180,17 +180,11 @@ impl Db {
                 result.push((workspace_id, roots))
             }
             
-            
             Ok(result)
-        });
-        
-        match res {
-            Ok(result) => result,
-            Err(err) => {
-                log::error!("Failed to get recent workspaces, err: {}", err);
-                Vec::new()
-            }
-        }        
+        }).unwrap_or_else(|err| {
+            log::error!("Failed to get recent workspaces, err: {}", err);
+            Vec::new()
+        })
     }
 }
 
@@ -210,14 +204,14 @@ where
             connection.prepare(
                 "DELETE FROM workspaces WHERE workspace_id = ?",
             )?
-            .bind(preexisting_id.0)?
+            .with_bindings(preexisting_id.0)?
             .exec()?;
         }
     }
 
     connection
         .prepare("DELETE FROM worktree_roots WHERE workspace_id = ?")?
-        .bind(workspace_id.0)?
+        .with_bindings(workspace_id.0)?
         .exec()?;
 
     for root in worktree_roots {
@@ -226,12 +220,12 @@ where
         // let path = root.as_ref().to_string_lossy().to_string();
 
         connection.prepare("INSERT INTO worktree_roots(workspace_id, worktree_root) VALUES (?, ?)")?
-            .bind((workspace_id.0, path))?
+            .with_bindings((workspace_id.0, path))?
             .exec()?;
     }
 
     connection.prepare("UPDATE workspaces SET last_opened_timestamp = CURRENT_TIMESTAMP WHERE workspace_id = ?")?
-        .bind(workspace_id.0)?
+        .with_bindings(workspace_id.0)?
         .exec()?;
 
     Ok(())
@@ -330,16 +324,11 @@ where
     // Make sure we bound the parameters correctly
     debug_assert!(worktree_roots.len() as i32 + 1 == stmt.parameter_count());
 
-    for i in 0..worktree_roots.len() {
-        let path = &worktree_roots[i].as_ref().as_os_str().as_bytes();
-        // If you need to debug this, here's the string parsing:
-        // let path = &worktree_roots[i].as_ref().to_string_lossy().to_string()
-        stmt.bind_value(*path, i as i32 + 1);
-    }
-    // No -1, because SQLite is 1 based
-    stmt.bind_value(worktree_roots.len(), worktree_roots.len() as i32 + 1)?;
-
-    stmt.maybe_row()
+    let root_bytes: Vec<&[u8]> = worktree_roots.iter()
+        .map(|root| root.as_ref().as_os_str().as_bytes()).collect();
+    
+    stmt.with_bindings((root_bytes, root_bytes.len()))?
+        .maybe_row()
         .map(|row| row.map(|id| WorkspaceId(id)))
 }
 

crates/sqlez/src/bindable.rs 🔗

@@ -207,3 +207,25 @@ impl<T: Column + Default + Copy, const COUNT: usize> Column for [T; COUNT] {
         Ok((array, current_index))
     }
 }
+
+impl<T: Bind> Bind for Vec<T> {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        let mut current_index = start_index;
+        for binding in self.iter() {
+            current_index = binding.bind(statement, current_index)?
+        }
+
+        Ok(current_index)
+    }
+}
+
+impl<T: Bind> Bind for &[T] {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        let mut current_index = start_index;
+        for binding in *self {
+            current_index = binding.bind(statement, current_index)?
+        }
+
+        Ok(current_index)
+    }
+}

crates/sqlez/src/connection.rs 🔗

@@ -149,7 +149,7 @@ mod test {
         connection
             .prepare("INSERT INTO text (text) VALUES (?);")
             .unwrap()
-            .bind(text)
+            .with_bindings(text)
             .unwrap()
             .exec()
             .unwrap();
@@ -185,8 +185,16 @@ mod test {
             .prepare("INSERT INTO test (text, integer, blob) VALUES (?, ?, ?)")
             .unwrap();
 
-        insert.bind(tuple1.clone()).unwrap().exec().unwrap();
-        insert.bind(tuple2.clone()).unwrap().exec().unwrap();
+        insert
+            .with_bindings(tuple1.clone())
+            .unwrap()
+            .exec()
+            .unwrap();
+        insert
+            .with_bindings(tuple2.clone())
+            .unwrap()
+            .exec()
+            .unwrap();
 
         assert_eq!(
             connection

crates/sqlez/src/migrations.rs 🔗

@@ -47,7 +47,7 @@ impl Migration {
                 WHERE domain = ?
                 ORDER BY step
                 "})?
-            .bind(self.domain)?
+            .with_bindings(self.domain)?
             .rows::<(String, usize, String)>()?;
 
         let mut store_completed_migration = connection
@@ -72,7 +72,7 @@ impl Migration {
 
             connection.exec(migration)?;
             store_completed_migration
-                .bind((self.domain, index, *migration))?
+                .with_bindings((self.domain, index, *migration))?
                 .exec()?;
         }
 
@@ -163,7 +163,7 @@ mod test {
                 .unwrap();
 
             store_completed_migration
-                .bind((domain, i, i.to_string()))
+                .with_bindings((domain, i, i.to_string()))
                 .unwrap()
                 .exec()
                 .unwrap();

crates/sqlez/src/savepoint.rs 🔗

@@ -76,14 +76,14 @@ mod tests {
         connection.with_savepoint("first", |save1| {
             save1
                 .prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
-                .bind((save1_text, 1))?
+                .with_bindings((save1_text, 1))?
                 .exec()?;
 
             assert!(save1
                 .with_savepoint("second", |save2| -> Result<Option<()>, anyhow::Error> {
                     save2
                         .prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
-                        .bind((save2_text, 2))?
+                        .with_bindings((save2_text, 2))?
                         .exec()?;
 
                     assert_eq!(
@@ -108,7 +108,7 @@ mod tests {
             save1.with_savepoint_rollback::<(), _>("second", |save2| {
                 save2
                     .prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
-                    .bind((save2_text, 2))?
+                    .with_bindings((save2_text, 2))?
                     .exec()?;
 
                 assert_eq!(
@@ -131,7 +131,7 @@ mod tests {
             save1.with_savepoint_rollback("second", |save2| {
                 save2
                     .prepare("INSERT INTO text(text, idx) VALUES (?, ?)")?
-                    .bind((save2_text, 2))?
+                    .with_bindings((save2_text, 2))?
                     .exec()?;
 
                 assert_eq!(

crates/sqlez/src/statement.rs 🔗

@@ -179,10 +179,9 @@ impl<'a> Statement<'a> {
         Ok(str::from_utf8(slice)?)
     }
 
-    pub fn bind_value<T: Bind>(&self, value: T, idx: i32) -> Result<()> {
-        debug_assert!(idx > 0);
-        value.bind(self, idx)?;
-        Ok(())
+    pub fn bind<T: Bind>(&self, value: T, index: i32) -> Result<i32> {
+        debug_assert!(index > 0);
+        value.bind(self, index)
     }
 
     pub fn column<T: Column>(&mut self) -> Result<T> {
@@ -203,8 +202,8 @@ impl<'a> Statement<'a> {
         }
     }
 
-    pub fn bind(&mut self, bindings: impl Bind) -> Result<&mut Self> {
-        self.bind_value(bindings, 1)?;
+    pub fn with_bindings(&mut self, bindings: impl Bind) -> Result<&mut Self> {
+        self.bind(bindings, 1)?;
         Ok(self)
     }
 

crates/sqlez/src/thread_safe_connection.rs 🔗

@@ -31,6 +31,8 @@ impl ThreadSafeConnection {
         self
     }
 
+    /// Migrations have to be run per connection because we fallback to memory
+    /// so this needs
     pub fn with_migrations(mut self, migrations: &'static [Migration]) -> Self {
         self.migrations = Some(migrations);
         self