spin_sdk/
sqlite.rs

1use super::wit::v2::sqlite;
2
3#[doc(inline)]
4pub use sqlite::{Error, Value};
5
6/// An open connection to a SQLite database.
7///
8/// # Examples
9///
10/// Load a set of rows from the default SQLite database, and iterate over them.
11///
12/// ```no_run
13/// use spin_sdk::sqlite::{Connection, Value};
14///
15/// # fn main() -> anyhow::Result<()> {
16/// # let min_age = 0;
17/// let db = Connection::open_default()?;
18///
19/// let query_result = db.execute(
20///     "SELECT * FROM users WHERE age >= ?",
21///     &[Value::Integer(min_age)]
22/// )?;
23///
24/// let name_index = query_result.columns.iter().position(|c| c == "name").unwrap();
25///
26/// for row in &query_result.rows {
27///     let name: &str = row.get(name_index).unwrap();
28///     println!("Found user {name}");
29/// }
30/// # Ok(())
31/// # }
32/// ```
33///
34/// Use the [QueryResult::rows()] wrapper to access a column by name. This is simpler and
35/// more readable but incurs a lookup on each access, so is not recommended when
36/// iterating a data set.
37///
38/// ```no_run
39/// # use spin_sdk::sqlite::{Connection, Value};
40/// # fn main() -> anyhow::Result<()> {
41/// # let user_id = 0;
42/// let db = Connection::open_default()?;
43/// let query_result = db.execute(
44///     "SELECT * FROM users WHERE id = ?",
45///     &[Value::Integer(user_id)]
46/// )?;
47/// let name = query_result.rows().next().and_then(|r| r.get::<&str>("name")).unwrap();
48/// # Ok(())
49/// # }
50/// ```
51///
52/// Perform an aggregate (scalar) operation over a named SQLite database. The result
53/// set contains a single column, with a single row.
54///
55/// ```no_run
56/// use spin_sdk::sqlite::Connection;
57///
58/// # fn main() -> anyhow::Result<()> {
59/// # let user_id = 0;
60/// let db = Connection::open("customer-data")?;
61/// let query_result = db.execute("SELECT COUNT(*) FROM users", &[])?;
62/// let count = query_result.rows.first().and_then(|r| r.get::<usize>(0)).unwrap();
63/// # Ok(())
64/// # }
65/// ```
66///
67/// Delete rows from a SQLite database. The usual [Connection::execute()] syntax
68/// is used but the query result is always empty.
69///
70/// ```no_run
71/// use spin_sdk::sqlite::{Connection, Value};
72///
73/// # fn main() -> anyhow::Result<()> {
74/// # let min_age = 18;
75/// let db = Connection::open("customer-data")?;
76/// db.execute("DELETE FROM users WHERE age < ?", &[Value::Integer(min_age)])?;
77/// # Ok(())
78/// # }
79/// ```
80#[doc(inline)]
81pub use sqlite::Connection;
82
83/// The result of a SQLite query issued with [Connection::execute()].
84///
85/// # Examples
86///
87/// Load a set of rows from the default SQLite database, and iterate over them.
88///
89/// ```no_run
90/// use spin_sdk::sqlite::{Connection, Value};
91///
92/// # fn main() -> anyhow::Result<()> {
93/// # let min_age = 0;
94/// let db = Connection::open_default()?;
95///
96/// let query_result = db.execute(
97///     "SELECT * FROM users WHERE age >= ?",
98///     &[Value::Integer(min_age)]
99/// )?;
100///
101/// let name_index = query_result.columns.iter().position(|c| c == "name").unwrap();
102///
103/// for row in &query_result.rows {
104///     let name: &str = row.get(name_index).unwrap();
105///     println!("Found user {name}");
106/// }
107/// # Ok(())
108/// # }
109/// ```
110///
111/// Use the [QueryResult::rows()] wrapper to access a column by name. This is simpler and
112/// more readable but incurs a lookup on each access, so is not recommended when
113/// iterating a data set.
114///
115/// ```no_run
116/// # use spin_sdk::sqlite::{Connection, Value};
117/// # fn main() -> anyhow::Result<()> {
118/// # let user_id = 0;
119/// let db = Connection::open_default()?;
120/// let query_result = db.execute(
121///     "SELECT * FROM users WHERE id = ?",
122///     &[Value::Integer(user_id)]
123/// )?;
124/// let name = query_result.rows().next().and_then(|r| r.get::<&str>("name")).unwrap();
125/// # Ok(())
126/// # }
127/// ```
128///
129/// Perform an aggregate (scalar) operation over a named SQLite database. The result
130/// set contains a single column, with a single row.
131///
132/// ```no_run
133/// use spin_sdk::sqlite::Connection;
134///
135/// # fn main() -> anyhow::Result<()> {
136/// # let user_id = 0;
137/// let db = Connection::open("customer-data")?;
138/// let query_result = db.execute("SELECT COUNT(*) FROM users", &[])?;
139/// let count = query_result.rows.first().and_then(|r| r.get::<usize>(0)).unwrap();
140/// # Ok(())
141/// # }
142/// ```
143#[doc(inline)]
144pub use sqlite::QueryResult;
145
146/// A database row result.
147///
148/// There are two representations of a SQLite row in the SDK. This type is obtained from
149/// the [field@QueryResult::rows] field, and provides index-based lookup or low-level access
150/// to row values via a vector. The [Row] type is useful for
151/// addressing elements by column name, and is obtained from the [QueryResult::rows()] function.
152///
153/// # Examples
154///
155/// Load a set of rows from the default SQLite database, and iterate over them selecting one
156/// field from each. The example caches the index of the desired field to avoid repeated lookup,
157/// making this more efficient than the [Row]-based equivalent at the expense of
158/// extra code and inferior readability.
159///
160/// ```no_run
161/// use spin_sdk::sqlite::{Connection, Value};
162///
163/// # fn main() -> anyhow::Result<()> {
164/// # let min_age = 0;
165/// let db = Connection::open_default()?;
166///
167/// let query_result = db.execute(
168///     "SELECT * FROM users WHERE age >= ?",
169///     &[Value::Integer(min_age)]
170/// )?;
171///
172/// let name_index = query_result.columns.iter().position(|c| c == "name").unwrap();
173///
174/// for row in &query_result.rows {
175///     let name: &str = row.get(name_index).unwrap();
176///     println!("Found user {name}");
177/// }
178/// # Ok(())
179/// # }
180/// ```
181#[doc(inline)]
182pub use sqlite::RowResult;
183
184impl sqlite::Connection {
185    /// Open a connection to the default database
186    pub fn open_default() -> Result<Self, Error> {
187        Self::open("default")
188    }
189}
190
191impl sqlite::QueryResult {
192    /// Get all the rows for this query result
193    pub fn rows(&self) -> impl Iterator<Item = Row<'_>> {
194        self.rows.iter().map(|r| Row {
195            columns: self.columns.as_slice(),
196            result: r,
197        })
198    }
199}
200
201/// A database row result.
202///
203/// There are two representations of a SQLite row in the SDK.  This type is useful for
204/// addressing elements by column name, and is obtained from the [QueryResult::rows()] function.
205/// The [RowResult] type is obtained from the [field@QueryResult::rows] field, and provides
206/// index-based lookup or low-level access to row values via a vector.
207pub struct Row<'a> {
208    columns: &'a [String],
209    result: &'a sqlite::RowResult,
210}
211
212impl<'a> Row<'a> {
213    /// Get a value by its column name. The value is converted to the target type.
214    ///
215    /// * SQLite integers are convertible to Rust integer types (i8, u8, i16, etc. including usize and isize) and bool.
216    /// * SQLite strings are convertible to Rust &str or &[u8] (encoded as UTF-8).
217    /// * SQLite reals are convertible to Rust f64.
218    /// * SQLite blobs are convertible to Rust &[u8] or &str (interpreted as UTF-8).
219    ///
220    /// If your code does not know the type in advance, use [RowResult] instead of `Row` to
221    /// access the underlying [Value] enum.
222    ///
223    /// # Examples
224    ///
225    /// ```no_run
226    /// use spin_sdk::sqlite::{Connection, Value};
227    ///
228    /// # fn main() -> anyhow::Result<()> {
229    /// # let user_id = 0;
230    /// let db = Connection::open_default()?;
231    /// let query_result = db.execute(
232    ///     "SELECT * FROM users WHERE id = ?",
233    ///     &[Value::Integer(user_id)]
234    /// )?;
235    /// let user_row = query_result.rows().next().unwrap();
236    ///
237    /// let name = user_row.get::<&str>("name").unwrap();
238    /// let age = user_row.get::<u16>("age").unwrap();
239    /// # Ok(())
240    /// # }
241    /// ```
242    pub fn get<T: TryFrom<&'a Value>>(&self, column: &str) -> Option<T> {
243        let i = self.columns.iter().position(|c| c == column)?;
244        self.result.get(i)
245    }
246}
247
248impl sqlite::RowResult {
249    /// Get a value by its column name. The value is converted to the target type.
250    ///
251    /// * SQLite integers are convertible to Rust integer types (i8, u8, i16, etc. including usize and isize) and bool.
252    /// * SQLite strings are convertible to Rust &str or &[u8] (encoded as UTF-8).
253    /// * SQLite reals are convertible to Rust f64.
254    /// * SQLite blobs are convertible to Rust &[u8] or &str (interpreted as UTF-8).
255    ///
256    /// To look up by name, you can use `QueryResult::rows()` or obtain the invoice from `QueryResult::columns`.
257    /// If you do not know the type of a value, access the underlying [Value] enum directly
258    /// via the [RowResult::values] field
259    ///
260    /// # Examples
261    ///
262    /// ```no_run
263    /// use spin_sdk::sqlite::{Connection, Value};
264    ///
265    /// # fn main() -> anyhow::Result<()> {
266    /// # let user_id = 0;
267    /// let db = Connection::open_default()?;
268    /// let query_result = db.execute(
269    ///     "SELECT name, age FROM users WHERE id = ?",
270    ///     &[Value::Integer(user_id)]
271    /// )?;
272    /// let user_row = query_result.rows.first().unwrap();
273    ///
274    /// let name = user_row.get::<&str>(0).unwrap();
275    /// let age = user_row.get::<u16>(1).unwrap();
276    /// # Ok(())
277    /// # }
278    /// ```
279    pub fn get<'a, T: TryFrom<&'a Value>>(&'a self, index: usize) -> Option<T> {
280        self.values.get(index).and_then(|c| c.try_into().ok())
281    }
282}
283
284impl<'a> TryFrom<&'a Value> for bool {
285    type Error = ();
286
287    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
288        match value {
289            Value::Integer(i) => Ok(*i != 0),
290            _ => Err(()),
291        }
292    }
293}
294
295macro_rules! int_conversions {
296    ($($t:ty),*) => {
297        $(impl<'a> TryFrom<&'a Value> for $t {
298            type Error = ();
299
300            fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
301                match value {
302                    Value::Integer(i) => (*i).try_into().map_err(|_| ()),
303                    _ => Err(()),
304                }
305            }
306        })*
307    };
308}
309
310int_conversions!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize);
311
312impl<'a> TryFrom<&'a Value> for f64 {
313    type Error = ();
314
315    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
316        match value {
317            Value::Real(f) => Ok(*f),
318            _ => Err(()),
319        }
320    }
321}
322
323impl<'a> TryFrom<&'a Value> for &'a str {
324    type Error = ();
325
326    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
327        match value {
328            Value::Text(s) => Ok(s.as_str()),
329            Value::Blob(b) => std::str::from_utf8(b).map_err(|_| ()),
330            _ => Err(()),
331        }
332    }
333}
334
335impl<'a> TryFrom<&'a Value> for &'a [u8] {
336    type Error = ();
337
338    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
339        match value {
340            Value::Blob(b) => Ok(b.as_slice()),
341            Value::Text(s) => Ok(s.as_bytes()),
342            _ => Err(()),
343        }
344    }
345}