Skip to main content

miden_node_db/sqlite/
codec.rs

1//! Column codec for the rusqlite-based SQLite framework.
2//!
3//! [`ToSqlValue`] and [`FromSqlValue`] are the per-column write/read codec for our domain types.
4//! They operate on [`DbValue`]/[`DbValueRef`], thin wrappers over rusqlite's value types, so that
5//! crates implementing a codec for their own types never have to name `rusqlite` directly.
6//!
7//! Most node types are stored as a BLOB via their `Serializable`/`Deserializable` impls; the
8//! [`impl_blob_codec!`](crate::impl_blob_codec) macro generates both traits for such a type. Scalar
9//! types map onto an SQLite `INTEGER`/`TEXT` and implement the traits directly (see the impls ported
10//! from the legacy `SqlTypeConvert` below).
11
12use std::rc::Rc;
13
14use rusqlite::ToSql;
15use rusqlite::types::{ToSqlOutput, Value, ValueRef};
16
17use crate::DatabaseError;
18
19// DB VALUE WRAPPERS
20// =================================================================================================
21
22/// An owned SQL value produced when binding a Rust value as a query parameter.
23///
24/// Wraps `rusqlite`'s value types so codec implementors never name `rusqlite`. A value is either a
25/// single column value or a list bound for a `rarray(?)` table-valued parameter (used by the
26/// cacheable IN-list helpers in [`in_list`](crate::sqlite::in_list)).
27#[derive(Debug, Clone)]
28pub enum DbValue {
29    /// A single SQL column value.
30    Single(Value),
31    /// A list of values bound via rusqlite's `array` extension for use with `rarray(?)`.
32    Array(Rc<Vec<Value>>),
33}
34
35impl DbValue {
36    /// Builds an `INTEGER` value.
37    pub fn integer(value: i64) -> Self {
38        Self::Single(Value::Integer(value))
39    }
40
41    /// Builds a `REAL` value.
42    pub fn real(value: f64) -> Self {
43        Self::Single(Value::Real(value))
44    }
45
46    /// Builds a `TEXT` value.
47    pub fn text(value: String) -> Self {
48        Self::Single(Value::Text(value))
49    }
50
51    /// Builds a `BLOB` value.
52    pub fn blob(value: Vec<u8>) -> Self {
53        Self::Single(Value::Blob(value))
54    }
55
56    /// Builds a `NULL` value.
57    pub fn null() -> Self {
58        Self::Single(Value::Null)
59    }
60
61    /// Builds a list value bound for a `rarray(?)` table-valued parameter.
62    pub(crate) fn array(values: Vec<Value>) -> Self {
63        Self::Array(Rc::new(values))
64    }
65}
66
67impl ToSql for DbValue {
68    fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
69        match self {
70            Self::Single(value) => value.to_sql(),
71            Self::Array(values) => values.to_sql(),
72        }
73    }
74}
75
76/// A borrowed SQL value handed to [`FromSqlValue`] when reading a column.
77///
78/// Wraps `rusqlite::types::ValueRef` so codec implementors never name `rusqlite`.
79#[derive(Debug, Clone, Copy)]
80pub struct DbValueRef<'a>(ValueRef<'a>);
81
82impl<'a> DbValueRef<'a> {
83    pub(crate) fn new(value: ValueRef<'a>) -> Self {
84        Self(value)
85    }
86
87    /// Reads the value as an `i64`.
88    pub fn as_i64(self) -> Result<i64, DatabaseError> {
89        self.0.as_i64().map_err(|err| DatabaseError::deserialization("i64", err))
90    }
91
92    /// Reads the value as a borrowed BLOB.
93    pub fn as_blob(self) -> Result<&'a [u8], DatabaseError> {
94        self.0.as_blob().map_err(|err| DatabaseError::deserialization("blob", err))
95    }
96
97    /// Reads the value as a borrowed string.
98    pub fn as_str(self) -> Result<&'a str, DatabaseError> {
99        self.0.as_str().map_err(|err| DatabaseError::deserialization("str", err))
100    }
101
102    /// Returns `true` if the value is SQL `NULL`.
103    pub fn is_null(self) -> bool {
104        matches!(self.0, ValueRef::Null)
105    }
106}
107
108// CODEC TRAITS
109// =================================================================================================
110
111/// Converts a Rust value into its SQL parameter representation (the write side of the codec).
112pub trait ToSqlValue {
113    /// Returns the SQL value bound for this Rust value.
114    fn to_sql_value(&self) -> DbValue;
115}
116
117/// Builds a Rust value from a SQL column value (the read side of the codec).
118pub trait FromSqlValue: Sized {
119    /// Reads `Self` from a SQL column value.
120    fn from_sql_value(value: DbValueRef<'_>) -> Result<Self, DatabaseError>;
121}
122
123// Forward `ToSqlValue` through references so callers can pass `&value` in a parameter slice.
124impl<T: ToSqlValue + ?Sized> ToSqlValue for &T {
125    fn to_sql_value(&self) -> DbValue {
126        (**self).to_sql_value()
127    }
128}
129
130// PRIMITIVE IMPLS
131// =================================================================================================
132
133impl ToSqlValue for i64 {
134    fn to_sql_value(&self) -> DbValue {
135        DbValue::integer(*self)
136    }
137}
138
139impl FromSqlValue for i64 {
140    fn from_sql_value(value: DbValueRef<'_>) -> Result<Self, DatabaseError> {
141        value.as_i64()
142    }
143}
144
145impl ToSqlValue for bool {
146    fn to_sql_value(&self) -> DbValue {
147        DbValue::integer(i64::from(*self))
148    }
149}
150
151impl FromSqlValue for bool {
152    fn from_sql_value(value: DbValueRef<'_>) -> Result<Self, DatabaseError> {
153        Ok(value.as_i64()? != 0)
154    }
155}
156
157impl ToSqlValue for Vec<u8> {
158    fn to_sql_value(&self) -> DbValue {
159        DbValue::blob(self.clone())
160    }
161}
162
163impl FromSqlValue for Vec<u8> {
164    fn from_sql_value(value: DbValueRef<'_>) -> Result<Self, DatabaseError> {
165        Ok(value.as_blob()?.to_vec())
166    }
167}
168
169impl ToSqlValue for str {
170    fn to_sql_value(&self) -> DbValue {
171        DbValue::text(self.to_owned())
172    }
173}
174
175impl ToSqlValue for String {
176    fn to_sql_value(&self) -> DbValue {
177        DbValue::text(self.clone())
178    }
179}
180
181impl FromSqlValue for String {
182    fn from_sql_value(value: DbValueRef<'_>) -> Result<Self, DatabaseError> {
183        Ok(value.as_str()?.to_owned())
184    }
185}
186
187impl<T: ToSqlValue> ToSqlValue for Option<T> {
188    fn to_sql_value(&self) -> DbValue {
189        match self {
190            Some(value) => value.to_sql_value(),
191            None => DbValue::null(),
192        }
193    }
194}
195
196impl<T: FromSqlValue> FromSqlValue for Option<T> {
197    fn from_sql_value(value: DbValueRef<'_>) -> Result<Self, DatabaseError> {
198        if value.is_null() {
199            Ok(None)
200        } else {
201            Ok(Some(T::from_sql_value(value)?))
202        }
203    }
204}
205
206// BLOB CODEC MACRO
207// =================================================================================================
208
209/// Generates [`ToSqlValue`](crate::sqlite::ToSqlValue) and
210/// [`FromSqlValue`](crate::sqlite::FromSqlValue) for types stored as a BLOB via their
211/// `Serializable`/`Deserializable` impls.
212///
213/// The generated impls call the exact same `to_bytes()`/`read_from_bytes()` used elsewhere, so the
214/// on-disk byte layout is unchanged.
215#[macro_export]
216macro_rules! impl_blob_codec {
217    ($($t:ty),+ $(,)?) => {
218        $(
219            impl $crate::sqlite::ToSqlValue for $t {
220                fn to_sql_value(&self) -> $crate::sqlite::DbValue {
221                    $crate::sqlite::DbValue::blob(
222                        ::miden_protocol::utils::serde::Serializable::to_bytes(self),
223                    )
224                }
225            }
226
227            impl $crate::sqlite::FromSqlValue for $t {
228                fn from_sql_value(
229                    value: $crate::sqlite::DbValueRef<'_>,
230                ) -> ::core::result::Result<Self, $crate::DatabaseError> {
231                    let bytes = value.as_blob()?;
232                    <$t as ::miden_protocol::utils::serde::Deserializable>::read_from_bytes(bytes)
233                        .map_err(|err| {
234                            $crate::DatabaseError::deserialization(::core::stringify!($t), err)
235                        })
236                }
237            }
238        )+
239    };
240}
241
242// Codec for the common protocol types stored as BLOBs. Shared by all node crates so that the orphan
243// rule does not force each consumer to redeclare them.
244impl_blob_codec!(
245    miden_protocol::block::BlockHeader,
246    miden_protocol::account::AccountId,
247    miden_protocol::transaction::TransactionId,
248    miden_protocol::note::Nullifier,
249    miden_protocol::Word,
250);