Switch to a lighter sqlite dependency

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-10-08 12:13:59 +02:00
parent e909e04c6c
commit b8c2ab5316
No known key found for this signature in database
5 changed files with 90 additions and 126 deletions

100
Cargo.lock generated
View File

@ -2,17 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -306,18 +295,6 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "fallible-iterator"
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]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.8.0" version = "1.8.0"
@ -419,18 +396,6 @@ name = "hashbrown"
version = "0.12.3" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
dependencies = [
"hashbrown",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
@ -555,17 +520,6 @@ dependencies = [
"pkg-config", "pkg-config",
] ]
[[package]]
name = "libsqlite3-sys"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f0455f2c1bc9a7caa792907026e469c1d91761fb0ea37cbb16427c77280cf35"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "libssh2-sys" name = "libssh2-sys"
version = "0.2.23" version = "0.2.23"
@ -837,11 +791,11 @@ dependencies = [
"quickcheck_macros", "quickcheck_macros",
"radicle-git-ext", "radicle-git-ext",
"radicle-ssh", "radicle-ssh",
"rusqlite",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.6", "sha2 0.10.6",
"siphasher", "siphasher",
"sqlite",
"tempfile", "tempfile",
"thiserror", "thiserror",
"zeroize", "zeroize",
@ -881,9 +835,9 @@ dependencies = [
"quickcheck", "quickcheck",
"quickcheck_macros", "quickcheck_macros",
"radicle", "radicle",
"rusqlite",
"serde", "serde",
"serde_json", "serde_json",
"sqlite",
"tempfile", "tempfile",
"thiserror", "thiserror",
] ]
@ -948,20 +902,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[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",
"smallvec",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.11" version = "1.0.11"
@ -1055,12 +995,6 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.4.7" version = "0.4.7"
@ -1071,6 +1005,36 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "sqlite"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2df8edd55685048550daaaf2be9024182f3523086cc86f7d50c136e55173e8c"
dependencies = [
"libc",
"sqlite3-sys",
]
[[package]]
name = "sqlite3-src"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1815a7a02c996eb8e5c64f61fcb6fd9b12e593ce265c512c5853b2513635691"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "sqlite3-sys"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d47c99824fc55360ba00caf28de0b8a0458369b832e016a64c13af0ad9fbb9ee"
dependencies = [
"libc",
"sqlite3-src",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.100" version = "1.0.100"

View File

@ -19,7 +19,7 @@ log = { version = "0.4.17", features = ["std"] }
nakamoto-net = { version = "0.3.0" } nakamoto-net = { version = "0.3.0" }
nakamoto-net-poll = { version = "0.3.0" } nakamoto-net-poll = { version = "0.3.0" }
nonempty = { version = "0.8.0", features = ["serialize"] } nonempty = { version = "0.8.0", features = ["serialize"] }
rusqlite = { version = "0.28.0", features = ["bundled"] } sqlite = { version = "0.27.0" }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] } serde_json = { version = "1", features = ["preserve_order"] }
tempfile = { version = "3.3.0" } tempfile = { version = "3.3.0" }

View File

@ -1,7 +1,8 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt;
use std::path::Path; use std::path::Path;
use rusqlite as sql; use sqlite as sql;
use thiserror::Error; use thiserror::Error;
use crate::prelude::{Id, NodeId}; use crate::prelude::{Id, NodeId};
@ -15,11 +16,16 @@ pub enum Error {
} }
/// Persistent file storage for a routing table. /// Persistent file storage for a routing table.
#[derive(Debug)]
pub struct Table { pub struct Table {
db: sql::Connection, db: sql::Connection,
} }
impl fmt::Debug for Table {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Table(..)")
}
}
impl Table { impl Table {
const SCHEMA: &str = include_str!("routing/schema.sql"); const SCHEMA: &str = include_str!("routing/schema.sql");
@ -27,15 +33,15 @@ impl Table {
/// if an existing store isn't found. /// if an existing store isn't found.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> { pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let db = sql::Connection::open(path)?; let db = sql::Connection::open(path)?;
db.execute(Self::SCHEMA, [])?; db.execute(Self::SCHEMA)?;
Ok(Self { db }) Ok(Self { db })
} }
/// Create a new in-memory routing table. /// Create a new in-memory routing table.
pub fn memory() -> Result<Self, Error> { pub fn memory() -> Result<Self, Error> {
let db = sql::Connection::open_in_memory()?; let db = sql::Connection::open(":memory:")?;
db.execute(Self::SCHEMA, [])?; db.execute(Self::SCHEMA)?;
Ok(Self { db }) Ok(Self { db })
} }
@ -57,36 +63,42 @@ impl Store for Table {
fn get(&self, id: &Id) -> Result<HashSet<NodeId>, Error> { fn get(&self, id: &Id) -> Result<HashSet<NodeId>, Error> {
let mut stmt = self let mut stmt = self
.db .db
.prepare("SELECT (node) FROM routing WHERE resource = ?")?; .prepare("SELECT (node) FROM routing WHERE resource = ?")?
let mut rows = stmt.query([id])?; .bind(1, id)?
.into_cursor();
let mut nodes = HashSet::new(); let mut nodes = HashSet::new();
while let Ok(Some(row)) = rows.next() { while let Some(Ok(row)) = stmt.next() {
let field = row.get(0)?; nodes.insert(row.get::<NodeId, _>(0));
nodes.insert(field);
} }
Ok(nodes) Ok(nodes)
} }
fn insert(&mut self, id: Id, node: NodeId) -> Result<bool, Error> { fn insert(&mut self, id: Id, node: NodeId) -> Result<bool, Error> {
let updated = self.db.execute( self.db
"INSERT INTO routing (resource, node, time) VALUES (?, ?, ?) ON CONFLICT DO NOTHING", .prepare(
(id, node, 0), "INSERT INTO routing (resource, node, time)
)?; VALUES (?, ?, ?)
ON CONFLICT DO NOTHING",
)?
.bind(1, &id)?
.bind(2, &node)?
.bind(3, 0)?
.next()?;
Ok(updated > 0) Ok(self.db.change_count() > 0)
} }
fn entries(&self) -> Result<Box<dyn Iterator<Item = (Id, NodeId)>>, Error> { fn entries(&self) -> Result<Box<dyn Iterator<Item = (Id, NodeId)>>, Error> {
let mut stmt = self let mut stmt = self
.db .db
.prepare("SELECT resource, node FROM routing ORDER BY resource")?; .prepare("SELECT resource, node FROM routing ORDER BY resource")?
let mut rows = stmt.query([])?; .into_cursor();
let mut entries = Vec::new(); let mut entries = Vec::new();
while let Ok(Some(row)) = rows.next() { while let Some(Ok(row)) = stmt.next() {
let id = row.get(0)?; let id = row.get(0);
let node = row.get(1)?; let node = row.get(1);
entries.push((id, node)); entries.push((id, node));
} }
@ -94,12 +106,13 @@ impl Store for Table {
} }
fn remove(&mut self, id: &Id, node: &NodeId) -> Result<bool, Error> { fn remove(&mut self, id: &Id, node: &NodeId) -> Result<bool, Error> {
let deleted = self.db.execute( self.db
"DELETE FROM routing WHERE resource = ? AND node = ?", .prepare("DELETE FROM routing WHERE resource = ? AND node = ?")?
(id, node), .bind(1, id)?
)?; .bind(2, node)?
.next()?;
Ok(deleted > 0) Ok(self.db.change_count() > 0)
} }
} }
@ -129,7 +142,7 @@ mod test {
} }
#[test] #[test]
fn test_iter() { fn test_entries() {
let ids = arbitrary::set::<Id>(6..9); let ids = arbitrary::set::<Id>(6..9);
let nodes = arbitrary::set::<NodeId>(6..9); let nodes = arbitrary::set::<NodeId>(6..9);
let mut db = Table::open(":memory:").unwrap(); let mut db = Table::open(":memory:").unwrap();
@ -160,13 +173,11 @@ mod test {
db.insert(*id, *node).unwrap(); db.insert(*id, *node).unwrap();
} }
} }
for id in &ids { for id in &ids {
for node in &nodes { for node in &nodes {
assert!(db.remove(id, node).unwrap()); assert!(db.remove(id, node).unwrap());
} }
} }
for id in &ids { for id in &ids {
assert!(db.get(id).unwrap().is_empty()); assert!(db.get(id).unwrap().is_empty());
} }

View File

@ -8,7 +8,7 @@ edition = "2021"
[features] [features]
default = [] default = []
test = ["quickcheck"] test = ["quickcheck"]
sql = ["rusqlite"] sql = ["sqlite"]
[dependencies] [dependencies]
base64 = { version= "0.13" } base64 = { version= "0.13" }
@ -28,7 +28,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] } serde_json = { version = "1", features = ["preserve_order"] }
siphasher = { version = "0.3.10" } siphasher = { version = "0.3.10" }
radicle-git-ext = { version = "0", features = ["serde"] } radicle-git-ext = { version = "0", features = ["serde"] }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true } sqlite = { version = "0.27.0", optional = true }
nonempty = { version = "0.8.0", features = ["serialize"] } nonempty = { version = "0.8.0", features = ["serialize"] }
tempfile = { version = "3.3.0" } tempfile = { version = "3.3.0" }
thiserror = { version = "1" } thiserror = { version = "1" }

View File

@ -1,48 +1,37 @@
use std::str;
use std::str::FromStr; use std::str::FromStr;
use rusqlite as sql; use sqlite as sql;
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; use sqlite::Value;
use crate::crypto::PublicKey; use crate::crypto::PublicKey;
use crate::identity::Id; use crate::identity::Id;
impl FromSql for Id { impl sql::ValueInto for Id {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { fn into(value: &Value) -> Option<Self> {
match value { match value {
ValueRef::Text(id) => { Value::String(id) => Id::from_str(id).ok(),
let id = str::from_utf8(id).map_err(|e| FromSqlError::Other(Box::new(e)))?; _ => None,
let id = Id::from_str(id).map_err(|e| FromSqlError::Other(Box::new(e)))?;
Ok(id)
}
_ => Err(FromSqlError::InvalidType),
} }
} }
} }
impl ToSql for Id { impl sqlite::Bindable for &Id {
fn to_sql(&self) -> sql::Result<ToSqlOutput<'_>> { fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> {
Ok(ToSqlOutput::from(self.to_string())) self.to_human().as_str().bind(stmt, i)
} }
} }
impl FromSql for PublicKey { impl sql::ValueInto for PublicKey {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { fn into(value: &Value) -> Option<Self> {
match value { match value {
ValueRef::Text(pk) => { Value::String(id) => PublicKey::from_str(id).ok(),
let pk = str::from_utf8(pk).map_err(|e| FromSqlError::Other(Box::new(e)))?; _ => None,
let pk = PublicKey::from_str(pk).map_err(|e| FromSqlError::Other(Box::new(e)))?;
Ok(pk)
}
_ => Err(FromSqlError::InvalidType),
} }
} }
} }
impl ToSql for PublicKey { impl sqlite::Bindable for &PublicKey {
fn to_sql(&self) -> sql::Result<ToSqlOutput<'_>> { fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> {
Ok(ToSqlOutput::from(self.to_string())) self.to_human().as_str().bind(stmt, i)
} }
} }