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}