Converted to using rusqlite

Mikayla Maki created

Change summary

Cargo.lock                        | 149 ++++++++------------------------
crates/client/src/telemetry.rs    |  14 +-
crates/db/Cargo.toml              |   4 
crates/db/migrations/001_init.sql |   4 
crates/db/sqlx-data.json          |   3 
crates/db/src/db.rs               |  46 ++++-----
crates/db/src/kvp.rs              |  35 +++---
crates/db/src/migrations.rs       |  10 ++
8 files changed, 92 insertions(+), 173 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -244,21 +244,6 @@ dependencies = [
  "futures-lite",
 ]
 
-[[package]]
-name = "async-global-executor"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0da5b41ee986eed3f524c380e6d64965aea573882a8907682ad100f7859305ca"
-dependencies = [
- "async-channel",
- "async-executor",
- "async-io",
- "async-lock",
- "blocking",
- "futures-lite",
- "once_cell",
-]
-
 [[package]]
 name = "async-io"
 version = "1.9.0"
@@ -338,33 +323,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "async-std"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
-dependencies = [
- "async-channel",
- "async-global-executor",
- "async-io",
- "async-lock",
- "async-process",
- "crossbeam-utils 0.8.12",
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-lite",
- "gloo-timers",
- "kv-log-macro",
- "log",
- "memchr",
- "once_cell",
- "pin-project-lite 0.2.9",
- "pin-utils",
- "slab",
- "wasm-bindgen-futures",
-]
-
 [[package]]
 name = "async-stream"
 version = "0.3.3"
@@ -1645,8 +1603,10 @@ dependencies = [
  "async-trait",
  "collections",
  "gpui",
+ "lazy_static",
  "parking_lot 0.11.2",
- "sqlx",
+ "rusqlite",
+ "rusqlite_migration",
  "tempdir",
 ]
 
@@ -1868,9 +1828,6 @@ name = "either"
 version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
-dependencies = [
- "serde",
-]
 
 [[package]]
 name = "encoding_rs"
@@ -1974,6 +1931,12 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
 [[package]]
 name = "fastrand"
 version = "1.8.0"
@@ -2041,18 +2004,6 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e"
 
-[[package]]
-name = "flume"
-version = "0.10.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
-dependencies = [
- "futures-core",
- "futures-sink",
- "pin-project",
- "spin 0.9.4",
-]
-
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -2307,17 +2258,6 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "futures-rustls"
-version = "0.22.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd"
-dependencies = [
- "futures-io",
- "rustls 0.20.6",
- "webpki 0.22.0",
-]
-
 [[package]]
 name = "futures-sink"
 version = "0.3.24"
@@ -2473,18 +2413,6 @@ dependencies = [
  "regex",
 ]
 
-[[package]]
-name = "gloo-timers"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
-dependencies = [
- "futures-channel",
- "futures-core",
- "js-sys",
- "wasm-bindgen",
-]
-
 [[package]]
 name = "go_to_line"
 version = "0.1.0"
@@ -3093,15 +3021,6 @@ dependencies = [
  "arrayvec 0.7.2",
 ]
 
-[[package]]
-name = "kv-log-macro"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
-dependencies = [
- "log",
-]
-
 [[package]]
 name = "language"
 version = "0.1.0"
@@ -3154,7 +3073,7 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 dependencies = [
- "spin 0.5.2",
+ "spin",
 ]
 
 [[package]]
@@ -3229,9 +3148,9 @@ dependencies = [
 
 [[package]]
 name = "libsqlite3-sys"
-version = "0.24.2"
+version = "0.25.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
+checksum = "9f0455f2c1bc9a7caa792907026e469c1d91761fb0ea37cbb16427c77280cf35"
 dependencies = [
  "cc",
  "pkg-config",
@@ -4825,7 +4744,7 @@ dependencies = [
  "cc",
  "libc",
  "once_cell",
- "spin 0.5.2",
+ "spin",
  "untrusted",
  "web-sys",
  "winapi 0.3.9",
@@ -4932,6 +4851,31 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "rusqlite"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "serde_json",
+ "smallvec",
+]
+
+[[package]]
+name = "rusqlite_migration"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eda44233be97aea786691f9f6f7ef230bcf905061f4012e90f4f39e6dcf31163"
+dependencies = [
+ "log",
+ "rusqlite",
+]
+
 [[package]]
 name = "rust-embed"
 version = "6.4.1"
@@ -5639,15 +5583,6 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
 
-[[package]]
-name = "spin"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
-dependencies = [
- "lock_api",
-]
-
 [[package]]
 name = "spsc-buffer"
 version = "0.1.1"
@@ -5693,10 +5628,8 @@ dependencies = [
  "dotenvy",
  "either",
  "event-listener",
- "flume",
  "futures-channel",
  "futures-core",
- "futures-executor",
  "futures-intrusive",
  "futures-util",
  "hashlink",
@@ -5706,7 +5639,6 @@ dependencies = [
  "indexmap",
  "itoa",
  "libc",
- "libsqlite3-sys",
  "log",
  "md-5",
  "memchr",
@@ -5742,12 +5674,9 @@ dependencies = [
  "dotenvy",
  "either",
  "heck 0.4.0",
- "hex",
  "once_cell",
  "proc-macro2",
  "quote",
- "serde",
- "serde_json",
  "sha2 0.10.6",
  "sqlx-core",
  "sqlx-rt",
@@ -5761,8 +5690,6 @@ version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396"
 dependencies = [
- "async-std",
- "futures-rustls",
  "once_cell",
  "tokio",
  "tokio-rustls",

crates/client/src/telemetry.rs 🔗

@@ -135,25 +135,21 @@ impl Telemetry {
         Some(self.state.lock().log_file.as_ref()?.path().to_path_buf())
     }
 
-    pub fn start(self: &Arc<Self>, db: Arc<Db>) {
+    pub fn start(self: &Arc<Self>, db: Arc<Mutex<Db>>) {
         let this = self.clone();
         self.executor
             .spawn(
                 async move {
-                    let device_id = if let Some(device_id) = db
-                        .read(["device_id"])?
-                        .into_iter()
-                        .flatten()
-                        .next()
-                        .and_then(|bytes| String::from_utf8(bytes).ok())
-                    {
+                    let db = db.lock();
+                    let device_id = if let Ok(device_id) = db.read_kvp("device_id") {
                         device_id
                     } else {
                         let device_id = Uuid::new_v4().to_string();
-                        db.write([("device_id", device_id.as_bytes())])?;
+                        db.write_kvp("device_id", &device_id)?;
                         device_id
                     };
 
+                    drop(db);
                     let device_id = Some(Arc::from(device_id));
                     let mut state = this.state.lock();
                     state.device_id = device_id.clone();

crates/db/Cargo.toml 🔗

@@ -15,7 +15,9 @@ collections = { path = "../collections" }
 anyhow = "1.0.57"
 async-trait = "0.1"
 parking_lot = "0.11.1"
-sqlx = { version = "0.6.2", features = ["sqlite", "macros", "offline", "json", "runtime-async-std-rustls"] }
+rusqlite = { version = "0.28.0", features = ["bundled", "serde_json"] }
+rusqlite_migration = "1.0.0"
+lazy_static = "1.4.0"
 
 [dev-dependencies]
 gpui = { path = "../gpui", features = ["test-support"] }

crates/db/src/db.rs 🔗

@@ -1,16 +1,17 @@
 mod kvp;
+mod migrations;
 
 use anyhow::Result;
-use sqlx::sqlite::SqliteConnectOptions;
-use sqlx::{Pool, Sqlite};
+use migrations::MIGRATIONS;
+use parking_lot::Mutex;
+use rusqlite::Connection;
 use std::path::Path;
-use std::str::FromStr;
 use std::sync::Arc;
 
 pub use kvp::*;
 
 pub struct Db {
-    pool: Pool<Sqlite>,
+    connecion: Connection,
     in_memory: bool,
 }
 
@@ -21,35 +22,26 @@ pub struct Db {
 impl Db {
     /// Open or create a database at the given file path. Falls back to in memory database if the
     /// database at the given path is corrupted
-    pub async fn open(path: &Path) -> Arc<Self> {
-        let options = SqliteConnectOptions::from_str(&format!(
-            "sqlite://{}",
-            path.to_string_lossy().to_string()
-        ))
-        .expect("database path should always be well formed")
-        .create_if_missing(true);
-
-        Self::initialize(options, false)
-            .await
-            .unwrap_or(Self::open_in_memory().await)
+    pub fn open(path: &Path) -> Result<Arc<Mutex<Self>>> {
+        let conn = Connection::open(path)?;
+
+        Self::initialize(conn, false).or_else(|_| Self::open_in_memory())
     }
 
     /// Open a in memory database for testing and as a fallback.
-    pub async fn open_in_memory() -> Arc<Self> {
-        let options = SqliteConnectOptions::from_str(":memory:")
-            .expect("Should always be able to create in memory database options");
+    pub fn open_in_memory() -> Result<Arc<Mutex<Self>>> {
+        let conn = Connection::open_in_memory()?;
 
-        Self::initialize(options, true)
-            .await
-            .expect("Should always be able to create an in memory database")
+        Self::initialize(conn, true)
     }
 
-    async fn initialize(options: SqliteConnectOptions, in_memory: bool) -> Result<Arc<Self>> {
-        let pool = Pool::<Sqlite>::connect_with(options).await?;
-
-        sqlx::migrate!().run(&pool).await?;
+    fn initialize(mut conn: Connection, in_memory: bool) -> Result<Arc<Mutex<Self>>> {
+        MIGRATIONS.to_latest(&mut conn)?;
 
-        Ok(Arc::new(Self { pool, in_memory }))
+        Ok(Arc::new(Mutex::new(Self {
+            connecion: conn,
+            in_memory,
+        })))
     }
 }
 
@@ -61,7 +53,7 @@ mod tests {
     #[gpui::test]
     fn test_db() {
         let dir = TempDir::new("db-test").unwrap();
-        let fake_db = Db::open_fake();
+        let fake_db = Db::open_in_memory().unwrap();
         let real_db = Db::open(&dir.path().join("test.db")).unwrap();
 
         for db in [&real_db, &fake_db] {

crates/db/src/kvp.rs 🔗

@@ -3,31 +3,30 @@ use anyhow::Result;
 use super::Db;
 
 impl Db {
-    pub async fn read_kvp(&self, key: &str) -> Result<String> {
-        let value = sqlx::query!("SELECT value FROM kv_store WHERE key = ?", key)
-            .fetch_one(&self.pool)
-            .await
-            .map(|res| res.value)?;
+    pub fn read_kvp(&self, key: &str) -> Result<String> {
+        let mut stmt = self
+            .connecion
+            .prepare_cached("SELECT value FROM kv_store WHERE key = (?)")?;
 
-        Ok(value)
+        Ok(stmt.query_row([key], |row| row.get(0))?)
     }
 
-    pub async fn delete_kvp(&self, key: &str) -> Result<()> {
-        sqlx::query!("DELETE FROM kv_store WHERE key = ?", key)
-            .execute(&self.pool)
-            .await?;
+    pub fn delete_kvp(&self, key: &str) -> Result<()> {
+        let mut stmt = self
+            .connecion
+            .prepare_cached("SELECT value FROM kv_store WHERE key = (?)")?;
+
+        stmt.execute([key])?;
 
         Ok(())
     }
 
-    pub async fn write_kvp(&self, key: &str, value: &str) -> Result<()> {
-        sqlx::query!(
-            "INSERT OR REPLACE INTO kv_store(key, value) VALUES (?, ?)",
-            key,
-            value
-        )
-        .execute(&self.pool)
-        .await?;
+    pub fn write_kvp(&self, key: &str, value: &str) -> Result<()> {
+        let mut stmt = self
+            .connecion
+            .prepare_cached("INSERT OR REPLACE INTO kv_store(key, value) VALUES ((?), (?))")?;
+
+        stmt.execute([key, value])?;
 
         Ok(())
     }

crates/db/src/migrations.rs 🔗

@@ -0,0 +1,10 @@
+use rusqlite_migration::{Migrations, M};
+
+lazy_static::lazy_static! {
+    pub static ref MIGRATIONS: Migrations<'static> = Migrations::new(vec![M::up(
+        "CREATE TABLE kv_store(
+            key TEXT PRIMARY KEY,
+            value TEXT NOT NULL
+        ) STRICT;",
+    )]);
+}