spin_sqlite_libsql/
lib.rs1use 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
8pub struct LazyLibSqlConnection {
10 url: String,
11 token: String,
12 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#[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(¶meters))
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}