spin_sqlite_libsql/
lib.rs

1use anyhow::Context;
2use async_trait::async_trait;
3use spin_factor_sqlite::Connection;
4use spin_world::spin::sqlite::sqlite as v3;
5use spin_world::spin::sqlite::sqlite::{self, RowResult};
6use tokio::sync::OnceCell;
7
8/// A lazy wrapper around a [`LibSqlConnection`] that implements the [`Connection`] trait.
9pub struct LazyLibSqlConnection {
10    url: String,
11    token: String,
12    // Since the libSQL client can only be created asynchronously, we wait until
13    // we're in the `Connection` implementation to create. Since we only want to do
14    // this once, we use a `OnceCell` to store it.
15    inner: OnceCell<LibSqlConnection>,
16}
17
18impl LazyLibSqlConnection {
19    pub fn new(url: String, token: String) -> Self {
20        Self {
21            url,
22            token,
23            inner: OnceCell::new(),
24        }
25    }
26
27    pub async fn get_or_create_connection(&self) -> Result<&LibSqlConnection, v3::Error> {
28        self.inner
29            .get_or_try_init(|| async {
30                LibSqlConnection::create(self.url.clone(), self.token.clone())
31                    .await
32                    .context("failed to create SQLite client")
33            })
34            .await
35            .map_err(|_| v3::Error::InvalidConnection)
36    }
37}
38
39#[async_trait]
40impl Connection for LazyLibSqlConnection {
41    async fn query(
42        &self,
43        query: &str,
44        parameters: Vec<v3::Value>,
45    ) -> Result<v3::QueryResult, v3::Error> {
46        let client = self.get_or_create_connection().await?;
47        client.query(query, parameters).await
48    }
49
50    async fn execute_batch(&self, statements: &str) -> anyhow::Result<()> {
51        let client = self.get_or_create_connection().await?;
52        client.execute_batch(statements).await
53    }
54
55    async fn changes(&self) -> Result<u64, sqlite::Error> {
56        let client = self.get_or_create_connection().await?;
57        Ok(client.changes())
58    }
59
60    async fn last_insert_rowid(&self) -> Result<i64, sqlite::Error> {
61        let client = self.get_or_create_connection().await?;
62        Ok(client.last_insert_rowid())
63    }
64
65    fn summary(&self) -> Option<String> {
66        Some(format!("libSQL at {}", self.url))
67    }
68}
69
70/// An open connection to a libSQL server.
71#[derive(Clone)]
72pub struct LibSqlConnection {
73    inner: libsql::Connection,
74}
75
76impl LibSqlConnection {
77    pub async fn create(url: String, token: String) -> anyhow::Result<Self> {
78        let db = libsql::Builder::new_remote(url, token).build().await?;
79        let inner = db.connect()?;
80        Ok(Self { inner })
81    }
82}
83
84impl LibSqlConnection {
85    pub async fn query(
86        &self,
87        query: &str,
88        parameters: Vec<sqlite::Value>,
89    ) -> Result<sqlite::QueryResult, sqlite::Error> {
90        let result = self
91            .inner
92            .query(query, convert_parameters(&parameters))
93            .await
94            .map_err(|e| sqlite::Error::Io(e.to_string()))?;
95
96        Ok(sqlite::QueryResult {
97            columns: columns(&result),
98            rows: convert_rows(result)
99                .await
100                .map_err(|e| sqlite::Error::Io(e.to_string()))?,
101        })
102    }
103
104    pub async fn execute_batch(&self, statements: &str) -> anyhow::Result<()> {
105        self.inner.execute_batch(statements).await?;
106
107        Ok(())
108    }
109
110    pub fn changes(&self) -> u64 {
111        self.inner.changes()
112    }
113
114    pub fn last_insert_rowid(&self) -> i64 {
115        self.inner.last_insert_rowid()
116    }
117}
118
119fn columns(rows: &libsql::Rows) -> Vec<String> {
120    (0..rows.column_count())
121        .map(|index| rows.column_name(index).unwrap_or("").to_owned())
122        .collect()
123}
124
125async fn convert_rows(mut rows: libsql::Rows) -> anyhow::Result<Vec<RowResult>> {
126    let mut result_rows = vec![];
127
128    let column_count = rows.column_count();
129
130    while let Some(row) = rows.next().await? {
131        result_rows.push(convert_row(row, column_count));
132    }
133
134    Ok(result_rows)
135}
136
137fn convert_row(row: libsql::Row, column_count: i32) -> RowResult {
138    let values = (0..column_count)
139        .map(|index| convert_value(row.get_value(index).unwrap()))
140        .collect();
141    RowResult { values }
142}
143
144fn convert_value(v: libsql::Value) -> sqlite::Value {
145    use libsql::Value;
146
147    match v {
148        Value::Null => sqlite::Value::Null,
149        Value::Integer(value) => sqlite::Value::Integer(value),
150        Value::Real(value) => sqlite::Value::Real(value),
151        Value::Text(value) => sqlite::Value::Text(value),
152        Value::Blob(value) => sqlite::Value::Blob(value),
153    }
154}
155
156fn convert_parameters(parameters: &[sqlite::Value]) -> Vec<libsql::Value> {
157    use libsql::Value;
158
159    parameters
160        .iter()
161        .map(|v| match v {
162            sqlite::Value::Integer(value) => Value::Integer(*value),
163            sqlite::Value::Real(value) => Value::Real(*value),
164            sqlite::Value::Text(t) => Value::Text(t.clone()),
165            sqlite::Value::Blob(b) => Value::Blob(b.clone()),
166            sqlite::Value::Null => Value::Null,
167        })
168        .collect()
169}