WIP almost compiling with sqlez

This commit is contained in:
Mikayla Maki 2022-11-01 17:26:03 -07:00
parent 777f05eb76
commit 3c1b747f64
8 changed files with 77 additions and 57 deletions

View file

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

View file

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

View file

@ -207,3 +207,25 @@ impl<T: Column + Default + Copy, const COUNT: usize> Column for [T; COUNT] {
Ok((array, current_index)) 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)
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -31,6 +31,8 @@ impl ThreadSafeConnection {
self 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 { pub fn with_migrations(mut self, migrations: &'static [Migration]) -> Self {
self.migrations = Some(migrations); self.migrations = Some(migrations);
self self