diff --git a/Cargo.lock b/Cargo.lock index 6118c0fd..84be53ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2811,9 +2811,9 @@ dependencies = [ [[package]] name = "sqlite" -version = "0.28.1" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d7dcfcc5c3247141692d47227842b3e9c3cf7fe8dd78d91786d76eb3f27b717" +checksum = "12e072cb5fb89b3fe5e9c9584676348feb503f9fb3ae829d9868171bc5372d48" dependencies = [ "libc", "sqlite3-sys", diff --git a/radicle-crypto/Cargo.toml b/radicle-crypto/Cargo.toml index c4f3f7f3..f57e5d39 100644 --- a/radicle-crypto/Cargo.toml +++ b/radicle-crypto/Cargo.toml @@ -18,7 +18,7 @@ cyphernet = { version = "0", optional = true } multibase = { version = "0.9.1" } serde = { version = "1", features = ["derive"] } sha2 = { version = "0.10.2" } -sqlite = { version = "0.28.1", optional = true } +sqlite = { version = "0.30.3", optional = true } thiserror = { version = "1" } zeroize = { version = "1.5.7" } diff --git a/radicle-crypto/src/lib.rs b/radicle-crypto/src/lib.rs index 076c0bdf..19ae9db6 100644 --- a/radicle-crypto/src/lib.rs +++ b/radicle-crypto/src/lib.rs @@ -351,27 +351,38 @@ impl<'a> From<&PublicKey> for git_ref_format::Component<'a> { } #[cfg(feature = "sqlite")] -impl sqlite::ValueInto for PublicKey { - fn into(value: &sqlite::Value) -> Option { - use sqlite::Value; - match value { - Value::String(id) => PublicKey::from_str(id).ok(), - _ => None, - } - } -} - -#[cfg(feature = "sqlite")] -impl From for sqlite::Value { - fn from(pk: PublicKey) -> Self { +impl From<&PublicKey> for sqlite::Value { + fn from(pk: &PublicKey) -> Self { sqlite::Value::String(pk.to_human()) } } #[cfg(feature = "sqlite")] -impl sqlite::Bindable for &PublicKey { - fn bind(self, stmt: &mut sqlite::Statement<'_>, i: usize) -> sqlite::Result<()> { - sqlite::Value::from(*self).bind(stmt, i) +impl TryFrom<&sqlite::Value> for PublicKey { + type Error = sqlite::Error; + + fn try_from(value: &sqlite::Value) -> Result { + match value { + sqlite::Value::String(s) => Self::from_str(s).map_err(|e| sqlite::Error { + code: None, + message: Some(e.to_string()), + }), + _ => Err(sqlite::Error { + code: None, + message: None, + }), + } + } +} + +#[cfg(feature = "sqlite")] +impl sqlite::BindableWithIndex for &PublicKey { + fn bind( + self, + stmt: &mut sqlite::Statement<'_>, + i: I, + ) -> sqlite::Result<()> { + sqlite::Value::from(self).bind(stmt, i) } } diff --git a/radicle-node/Cargo.toml b/radicle-node/Cargo.toml index 42f7309b..3fa8a513 100644 --- a/radicle-node/Cargo.toml +++ b/radicle-node/Cargo.toml @@ -23,7 +23,7 @@ nakamoto-net = { version = "0.3.0" } nakamoto-net-poll = { version = "0.3.0" } nonempty = { version = "0.8.0", features = ["serialize"] } qcheck = { version = "1", default-features = false, optional = true } -sqlite = { version = "0.28.1" } +sqlite = { version = "0.30.3" } sqlite3-src = { version = "0.4.0", features = ["bundled"] } # Ensures static linking scrypt = { version = "0.10.0", default-features = false } serde = { version = "1", features = ["derive"] } diff --git a/radicle-node/src/address/store.rs b/radicle-node/src/address/store.rs index 48ba31e5..0fae0c8c 100644 --- a/radicle-node/src/address/store.rs +++ b/radicle-node/src/address/store.rs @@ -62,24 +62,24 @@ impl Store for Book { .db .prepare("SELECT features, alias, timestamp FROM nodes WHERE id = ?")?; - stmt.bind(1, node)?; + stmt.bind((1, node))?; - if let Some(Ok(row)) = stmt.into_cursor().next() { - let features = row.get::("features"); - let alias = row.get::("alias"); - let timestamp = row.get::("timestamp") as Timestamp; + if let Some(Ok(row)) = stmt.into_iter().next() { + let features = row.read::("features"); + let alias = row.read::<&str, _>("alias").to_owned(); + let timestamp = row.read::("timestamp") as Timestamp; let mut addrs = Vec::new(); let mut stmt = self .db .prepare("SELECT type, value, source FROM addresses WHERE node = ?")?; - stmt.bind(1, node)?; + stmt.bind((1, node))?; - for row in stmt.into_cursor() { + for row in stmt.into_iter() { let row = row?; - let _typ = row.get::("type"); - let addr = row.get::("value"); - let source = row.get::("source"); + let _typ = row.read::("type"); + let addr = row.read::("value"); + let source = row.read::("source"); addrs.push(KnownAddress { addr, @@ -104,11 +104,11 @@ impl Store for Book { let row = self .db .prepare("SELECT COUNT(*) FROM addresses")? - .into_cursor() + .into_iter() .next() .unwrap() .unwrap(); - let count = row.get::(0) as usize; + let count = row.read::(0) as usize; Ok(count) } @@ -130,10 +130,10 @@ impl Store for Book { WHERE timestamp < ?4", )?; - stmt.bind(1, node)?; - stmt.bind(2, features)?; - stmt.bind(3, alias)?; - stmt.bind(4, timestamp as i64)?; + stmt.bind((1, node))?; + stmt.bind((2, features))?; + stmt.bind((3, alias))?; + stmt.bind((4, timestamp as i64))?; stmt.next()?; for addr in addrs { @@ -144,11 +144,11 @@ impl Store for Book { SET timestamp = ?5 WHERE timestamp < ?5", )?; - stmt.bind(1, node)?; - stmt.bind(2, AddressType::from(&addr.addr))?; - stmt.bind(3, addr.addr)?; - stmt.bind(4, addr.source)?; - stmt.bind(5, timestamp as i64)?; + stmt.bind((1, node))?; + stmt.bind((2, AddressType::from(&addr.addr)))?; + stmt.bind((3, addr.addr))?; + stmt.bind((4, addr.source))?; + stmt.bind((5, timestamp as i64))?; stmt.next()?; } Ok(db.change_count() > 0) @@ -159,13 +159,13 @@ impl Store for Book { fn remove(&mut self, node: &NodeId) -> Result { transaction(&self.db, move |db| { db.prepare("DELETE FROM nodes WHERE id = ?")? - .into_cursor() - .bind(&[(*node).into()])? + .into_iter() + .bind(&[node][..])? .next(); db.prepare("DELETE FROM addresses WHERE node = ?")? - .into_cursor() - .bind(&[(*node).into()])? + .into_iter() + .bind(&[node][..])? .next(); Ok(db.change_count() > 0) @@ -177,14 +177,14 @@ impl Store for Book { let mut stmt = self .db .prepare("SELECT node, type, value, source FROM addresses ORDER BY node")? - .into_cursor(); + .into_iter(); let mut entries = Vec::new(); while let Some(Ok(row)) = stmt.next() { - let node = row.get("node"); - let _typ = row.get::("type"); - let addr = row.get::("value"); - let source = row.get::("source"); + let node = row.read::("node"); + let _typ = row.read::("type"); + let addr = row.read::("value"); + let source = row.read::("source"); entries.push(( node, @@ -229,37 +229,53 @@ pub trait Store { fn entries(&self) -> Result>, Error>; } -impl sql::ValueInto for Address { - fn into(value: &sql::Value) -> Option { +impl TryFrom<&sql::Value> for Address { + type Error = sql::Error; + + fn try_from(value: &sql::Value) -> Result { match value { - sql::Value::String(s) => Address::from_str(s.as_str()).ok(), - _ => None, + sql::Value::String(s) => Address::from_str(s.as_str()).map_err(|_| sql::Error { + code: None, + message: None, + }), + _ => Err(sql::Error { + code: None, + message: None, + }), } } } -impl sql::Bindable for Address { - fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> { +impl sql::BindableWithIndex for Address { + fn bind(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> { self.to_string().bind(stmt, i) } } -impl sql::ValueInto for Source { - fn into(value: &sql::Value) -> Option { +impl TryFrom<&sql::Value> for Source { + type Error = sql::Error; + + fn try_from(value: &sql::Value) -> Result { match value { sql::Value::String(s) => match s.as_str() { - "dns" => Some(Source::Dns), - "peer" => Some(Source::Peer), - "imported" => Some(Source::Imported), - _ => None, + "dns" => Ok(Source::Dns), + "peer" => Ok(Source::Peer), + "imported" => Ok(Source::Imported), + _ => Err(sql::Error { + code: None, + message: None, + }), }, - _ => None, + _ => Err(sql::Error { + code: None, + message: None, + }), } } } -impl sql::Bindable for Source { - fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> { +impl sql::BindableWithIndex for Source { + fn bind(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> { match self { Self::Dns => "dns".bind(stmt, i), Self::Peer => "peer".bind(stmt, i), @@ -268,23 +284,31 @@ impl sql::Bindable for Source { } } -impl sql::ValueInto for AddressType { - fn into(value: &sql::Value) -> Option { +impl TryFrom<&sql::Value> for AddressType { + type Error = sql::Error; + + fn try_from(value: &sql::Value) -> Result { match value { sql::Value::String(s) => match s.as_str() { - "ipv4" => Some(AddressType::Ipv4), - "ipv6" => Some(AddressType::Ipv6), - "hostname" => Some(AddressType::Hostname), - "onion" => Some(AddressType::Onion), - _ => None, + "ipv4" => Ok(AddressType::Ipv4), + "ipv6" => Ok(AddressType::Ipv6), + "hostname" => Ok(AddressType::Hostname), + "onion" => Ok(AddressType::Onion), + _ => Err(sql::Error { + code: None, + message: None, + }), }, - _ => None, + _ => Err(sql::Error { + code: None, + message: None, + }), } } } -impl sql::Bindable for AddressType { - fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> { +impl sql::BindableWithIndex for AddressType { + fn bind(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> { match self { Self::Ipv4 => "ipv4".bind(stmt, i), Self::Ipv6 => "ipv6".bind(stmt, i), diff --git a/radicle-node/src/service/routing.rs b/radicle-node/src/service/routing.rs index 11aeb469..f59a9841 100644 --- a/radicle-node/src/service/routing.rs +++ b/radicle-node/src/service/routing.rs @@ -82,11 +82,11 @@ impl Store for Table { let mut stmt = self .db .prepare("SELECT (node) FROM routing WHERE resource = ?")?; - stmt.bind(1, id)?; + stmt.bind((1, id))?; let mut nodes = HashSet::new(); - for row in stmt.into_cursor() { - nodes.insert(row?.get::("node")); + for row in stmt.into_iter() { + nodes.insert(row?.read::("node")); } Ok(nodes) } @@ -95,11 +95,11 @@ impl Store for Table { let mut stmt = self .db .prepare("SELECT resource FROM routing WHERE node = ?")?; - stmt.bind(1, node)?; + stmt.bind((1, node))?; let mut resources = HashSet::new(); - for row in stmt.into_cursor() { - resources.insert(row?.get::("resource")); + for row in stmt.into_iter() { + resources.insert(row?.read::("resource")); } Ok(resources) } @@ -109,11 +109,11 @@ impl Store for Table { .db .prepare("SELECT (time) FROM routing WHERE resource = ? AND node = ?")?; - stmt.bind(1, id)?; - stmt.bind(2, node)?; + stmt.bind((1, id))?; + stmt.bind((2, node))?; - if let Some(Ok(row)) = stmt.into_cursor().next() { - return Ok(Some(row.get::("time") as Timestamp)); + if let Some(Ok(row)) = stmt.into_iter().next() { + return Ok(Some(row.read::("time") as Timestamp)); } Ok(None) } @@ -128,9 +128,9 @@ impl Store for Table { WHERE time < ?3", )?; - stmt.bind(1, &id)?; - stmt.bind(2, &node)?; - stmt.bind(3, time)?; + stmt.bind((1, &id))?; + stmt.bind((2, &node))?; + stmt.bind((3, time))?; stmt.next()?; Ok(self.db.change_count() > 0) @@ -140,12 +140,12 @@ impl Store for Table { let mut stmt = self .db .prepare("SELECT resource, node FROM routing ORDER BY resource")? - .into_cursor(); + .into_iter(); let mut entries = Vec::new(); while let Some(Ok(row)) = stmt.next() { - let id = row.get("resource"); - let node = row.get("node"); + let id = row.read("resource"); + let node = row.read("node"); entries.push((id, node)); } @@ -157,8 +157,8 @@ impl Store for Table { .db .prepare("DELETE FROM routing WHERE resource = ? AND node = ?")?; - stmt.bind(1, id)?; - stmt.bind(2, node)?; + stmt.bind((1, id))?; + stmt.bind((2, node))?; stmt.next()?; Ok(self.db.change_count() > 0) @@ -167,10 +167,10 @@ impl Store for Table { fn len(&self) -> Result { let stmt = self.db.prepare("SELECT COUNT(1) FROM routing")?; let count: i64 = stmt - .into_cursor() + .into_iter() .next() .expect("COUNT will always return a single row")? - .get(0); + .read(0); let count: usize = count.try_into().map_err(|_| Error::UnitOverflow)?; Ok(count) } @@ -186,8 +186,8 @@ impl Store for Table { "DELETE FROM routing WHERE rowid IN (SELECT rowid FROM routing WHERE time < ? LIMIT ?)", )?; - stmt.bind(1, oldest)?; - stmt.bind(2, limit)?; + stmt.bind((1, oldest))?; + stmt.bind((2, limit))?; stmt.next()?; Ok(self.db.change_count()) diff --git a/radicle/Cargo.toml b/radicle/Cargo.toml index ac38e1a4..5ffe3445 100644 --- a/radicle/Cargo.toml +++ b/radicle/Cargo.toml @@ -27,7 +27,7 @@ serde = { version = "1", features = ["derive"] } serde_json = { version = "1", features = ["preserve_order"] } siphasher = { version = "0.3.10" } radicle-git-ext = { version = "0", features = ["serde"] } -sqlite = { version = "0.28.1", optional = true } +sqlite = { version = "0.30.3", optional = true } nonempty = { version = "0.8.0", features = ["serialize"] } tempfile = { version = "3.3.0" } thiserror = { version = "1" } diff --git a/radicle/src/sql.rs b/radicle/src/sql.rs index f0162793..b6be543e 100644 --- a/radicle/src/sql.rs +++ b/radicle/src/sql.rs @@ -7,32 +7,45 @@ use sqlite::Value; use crate::identity::Id; use crate::node; -impl sql::ValueInto for Id { - fn into(value: &Value) -> Option { +impl TryFrom<&Value> for Id { + type Error = sql::Error; + + fn try_from(value: &Value) -> Result { match value { - Value::String(id) => Id::from_str(id).ok(), - _ => None, + Value::String(id) => Id::from_str(id).map_err(|e| sql::Error { + code: None, + message: Some(e.to_string()), + }), + _ => Err(sql::Error { + code: None, + message: None, + }), } } } -impl sqlite::Bindable for &Id { - fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> { +impl sqlite::BindableWithIndex for &Id { + fn bind(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> { self.to_human().as_str().bind(stmt, i) } } -impl sql::Bindable for node::Features { - fn bind(self, stmt: &mut sql::Statement<'_>, i: usize) -> sql::Result<()> { +impl sql::BindableWithIndex for node::Features { + fn bind(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> { (*self.deref() as i64).bind(stmt, i) } } -impl sql::ValueInto for node::Features { - fn into(value: &Value) -> Option { +impl TryFrom<&Value> for node::Features { + type Error = sql::Error; + + fn try_from(value: &Value) -> Result { match value { - Value::Integer(bits) => Some(node::Features::from(*bits as u64)), - _ => None, + Value::Integer(bits) => Ok(node::Features::from(*bits as u64)), + _ => Err(sql::Error { + code: None, + message: None, + }), } } }