geekorm_core/backends/
libsql.rs

1//! # libsql
2//!
3//! This module contains the implementation for the `GeekConnection` trait for the `libsql` crate.
4//!
5//! ## LibSQL with timeout and retry
6//!
7//! Wrapper for a `libsql::Connection` that implements the `GeekConnection` trait.
8//!
9//! ```no_run
10//! # #[cfg(all(feature = "libsql", feature = "backends-tokio"))] {
11//! use std::sync::Arc;
12//! use geekorm::prelude::*;
13//!
14//! #[derive(Table, Clone, Default, serde::Serialize, serde::Deserialize)]
15//! struct Users {
16//!     #[geekorm(primary_key, auto_increment)]
17//!     id: PrimaryKey<i32>,
18//!     #[geekorm(unique)]
19//!     username: String,
20//! }
21//!
22//! #[tokio::main]
23//! async fn main() -> anyhow::Result<()> {
24//!     let database = libsql::Builder::new_local(":memory:").build().await?;
25//!     let connection = Arc::new(tokio::sync::Mutex::new(database.connect().unwrap()));
26//!
27//!     Users::create_table(&connection).await?;
28//!
29//!     let users = vec!["geekmasher", "bob", "alice", "eve", "mallory", "trent"];
30//!     for user in users {
31//!         let mut new_user = Users::new(user);
32//!         new_user.save(&connection).await?;
33//!     }
34//!     Ok(())
35//! }
36//! # }
37//! ```
38
39use libsql::{de, params::IntoValue};
40#[cfg(feature = "log")]
41use log::{debug, error};
42use serde::{Serialize, de::DeserializeOwned};
43use std::collections::HashMap;
44
45use crate::{
46    GeekConnection, QueryBuilderTrait, TableBuilder, Value, Values, builder::models::QueryType,
47};
48
49#[cfg(feature = "backends-tokio")]
50mod mutex;
51
52impl GeekConnection for libsql::Connection {
53    type Connection = libsql::Connection;
54
55    async fn create_table<T>(connection: &Self::Connection) -> Result<(), crate::Error>
56    where
57        T: TableBuilder + QueryBuilderTrait + Sized + Serialize + DeserializeOwned,
58    {
59        let query = T::query_create().build()?;
60        #[cfg(feature = "log")]
61        {
62            debug!("Create Table Query :: {:?}", query.to_str());
63        }
64        connection.execute(query.to_str(), ()).await.map_err(|e| {
65            crate::Error::QuerySyntaxError {
66                error: e.to_string(),
67                query: query.to_string(),
68            }
69        })?;
70        Ok(())
71    }
72
73    async fn row_count(
74        connection: &Self::Connection,
75        query: crate::Query,
76    ) -> Result<i64, crate::Error> {
77        #[cfg(feature = "log")]
78        {
79            debug!("Row Count Query :: {:?}", query.to_str());
80        }
81        let mut statement = connection.prepare(query.to_str()).await.map_err(|e| {
82            crate::Error::QuerySyntaxError {
83                error: e.to_string(),
84                query: query.to_string(),
85            }
86        })?;
87
88        let parameters: Vec<libsql::Value> = convert_values(&query)?;
89
90        let mut rows =
91            statement
92                .query(parameters)
93                .await
94                .map_err(|e| crate::Error::LibSQLError {
95                    error: e.to_string(),
96                    query: query.to_string(),
97                })?;
98
99        let row = match rows.next().await.map_err(|e| crate::Error::LibSQLError {
100            error: e.to_string(),
101            query: query.to_string(),
102        })? {
103            Some(row) => row,
104            None => {
105                #[cfg(feature = "log")]
106                {
107                    error!("Error fetching row count");
108                }
109                return Err(crate::Error::LibSQLError {
110                    error: "Error fetching row count".to_string(),
111                    query: query.to_string(),
112                });
113            }
114        };
115        // Get the first row
116        row.get(0).map_err(|e| crate::Error::LibSQLError {
117            error: e.to_string(),
118            query: query.to_string(),
119        })
120    }
121
122    async fn query<T>(
123        connection: &Self::Connection,
124        query: crate::Query,
125    ) -> Result<Vec<T>, crate::Error>
126    where
127        T: serde::de::DeserializeOwned,
128    {
129        #[cfg(feature = "log")]
130        {
131            debug!("Query :: {:?}", query.to_str());
132        }
133
134        let mut statement = connection.prepare(query.to_str()).await.map_err(|e| {
135            crate::Error::QuerySyntaxError {
136                error: e.to_string(),
137                query: query.to_string(),
138            }
139        })?;
140
141        let parameters: Vec<libsql::Value> = convert_values(&query)?;
142
143        #[cfg(feature = "log")]
144        {
145            debug!("Parameters :: {:?}", parameters.clone());
146        }
147
148        // Execute the query
149        let mut rows =
150            statement
151                .query(parameters)
152                .await
153                .map_err(|e| crate::Error::LibSQLError {
154                    error: e.to_string(),
155                    query: query.to_string(),
156                })?;
157
158        let mut results = Vec::new();
159
160        while let Some(row) = rows.next().await.map_err(|e| crate::Error::LibSQLError {
161            error: e.to_string(),
162            query: query.to_string(),
163        })? {
164            results.push(de::from_row::<T>(&row).map_err(|e| {
165                #[cfg(feature = "log")]
166                {
167                    error!("Error deserializing row: `{}`", e);
168                }
169                crate::Error::SerdeError(e.to_string())
170            })?);
171        }
172
173        Ok(results)
174    }
175
176    async fn query_first<T>(
177        connection: &Self::Connection,
178        query: crate::Query,
179    ) -> Result<T, crate::Error>
180    where
181        T: serde::de::DeserializeOwned,
182    {
183        // TODO: Should we always make sure the query limit is set to 1?
184        if query.query_type == QueryType::Update {
185            #[cfg(feature = "log")]
186            {
187                error!(
188                    "Query type is an `update`, use execute() instead as it does not return a row"
189                );
190            }
191            return Err(crate::Error::LibSQLError {
192                error: "Query type is an `update`".to_string(),
193                query: query.to_string(),
194            });
195        }
196
197        let mut statement = connection.prepare(query.to_str()).await.map_err(|e| {
198            crate::Error::QuerySyntaxError {
199                error: e.to_string(),
200                query: query.to_string(),
201            }
202        })?;
203
204        // Convert the values to libsql::Value
205        let parameters: Vec<libsql::Value> = convert_values(&query)?;
206
207        #[cfg(feature = "log")]
208        {
209            debug!("Query :: {:?}", query.to_str());
210            debug!("Parameters :: {:?}", parameters.clone());
211        }
212
213        // Execute the query
214        let mut rows =
215            statement
216                .query(parameters)
217                .await
218                .map_err(|e| crate::Error::LibSQLError {
219                    error: e.to_string(),
220                    query: query.to_string(),
221                })?;
222
223        let row: libsql::Row = match rows.next().await? {
224            Some(row) => row,
225            None => {
226                #[cfg(feature = "log")]
227                {
228                    error!("No rows found for query: `{}`", query.to_str());
229                }
230                return Err(crate::Error::NoRowsFound {
231                    query: query.to_string(),
232                });
233            }
234        };
235
236        de::from_row::<T>(&row).map_err(|e| {
237            #[cfg(feature = "log")]
238            {
239                error!("Error deserializing row: `{}`", e);
240            }
241            crate::Error::SerdeError(e.to_string())
242        })
243    }
244
245    async fn execute(
246        connection: &Self::Connection,
247        query: crate::Query,
248    ) -> Result<(), crate::Error> {
249        // Convert the values to libsql::Value
250        let parameters: Vec<libsql::Value> = convert_values(&query)?;
251
252        connection
253            .execute(query.to_str(), parameters)
254            .await
255            .map_err(|e| crate::Error::QuerySyntaxError {
256                error: e.to_string(),
257                query: query.to_string(),
258            })?;
259        Ok(())
260    }
261
262    async fn batch(connection: &Self::Connection, query: crate::Query) -> Result<(), crate::Error> {
263        connection
264            .execute_batch(query.to_str())
265            .await
266            .map_err(|e| crate::Error::QuerySyntaxError {
267                error: e.to_string(),
268                query: query.to_string(),
269            })?;
270        Ok(())
271    }
272
273    async fn query_raw(
274        connection: &Self::Connection,
275        query: crate::Query,
276    ) -> Result<Vec<HashMap<String, Value>>, crate::Error> {
277        let params = convert_values(&query)?;
278
279        let mut statement = connection.prepare(query.to_str()).await.map_err(|e| {
280            crate::Error::QuerySyntaxError {
281                error: e.to_string(),
282                query: query.to_string(),
283            }
284        })?;
285
286        #[cfg(feature = "log")]
287        {
288            debug!("Query :: {:?}", query.to_str());
289            debug!("Parameters :: {:?}", params);
290        }
291
292        let mut rows = statement
293            .query(params)
294            .await
295            .map_err(|e| crate::Error::LibSQLError {
296                error: e.to_string(),
297                query: query.to_string(),
298            })?;
299
300        let mut results: Vec<HashMap<String, Value>> = Vec::new();
301
302        while let Some(row) = rows.next().await? {
303            let mut values: HashMap<String, Value> = HashMap::new();
304
305            for (index, column_name) in query.columns.iter().enumerate() {
306                let value = row.get_value(index as i32).unwrap();
307                values.insert(column_name.to_string(), value.into());
308            }
309            results.push(values);
310        }
311
312        Ok(results)
313    }
314}
315
316fn convert_values(query: &crate::Query) -> Result<Vec<libsql::Value>, crate::Error> {
317    let mut parameters: Vec<libsql::Value> = Vec::new();
318
319    // TODO(geekmasher): This is awful, need to refactor this
320    let values: &Values = match query.query_type {
321        QueryType::Insert | QueryType::Update => &query.parameters,
322        _ => &query.values,
323    };
324
325    for (column_name, value) in &values.values {
326        // Check if the column exists in the table
327        // The column_name could be in another table not part of the query (joins)
328        if let Some(column) = query.table.columns.get(column_name.as_str()) {
329            // Skip auto increment columns if the query is an insert
330            if query.query_type == QueryType::Insert && column.column_type.is_auto_increment() {
331                continue;
332            } else if query.query_type == QueryType::Update && column.column_type.is_primary_key() {
333                continue;
334            }
335        }
336
337        #[cfg(feature = "log")]
338        {
339            log::trace!("LIBSQL - Column('{}', '{}')", column_name, value);
340        }
341
342        parameters.push(
343            value
344                .clone()
345                .into_value()
346                .map_err(|e| crate::Error::LibSQLError {
347                    error: format!("Error converting value - {}", e),
348                    query: query.to_string(),
349                })?,
350        );
351    }
352    Ok(parameters)
353}
354
355/// Convert LibSQL Error to GeekORM Error
356impl From<libsql::Error> for crate::Error {
357    fn from(value: libsql::Error) -> Self {
358        #[cfg(feature = "log")]
359        {
360            log::error!("LibSQL Error: `{}`", value);
361        }
362
363        crate::Error::LibSQLError {
364            error: value.to_string(),
365            query: String::new(),
366        }
367    }
368}
369
370impl From<(libsql::Error, String)> for crate::Error {
371    fn from(value: (libsql::Error, String)) -> Self {
372        #[cfg(feature = "log")]
373        {
374            log::error!("LibSQL Error: `{}`", value.0);
375            log::error!("LibSQL Error Query: `{}`", value.1);
376        }
377
378        crate::Error::LibSQLError {
379            error: value.0.to_string(),
380            query: value.1,
381        }
382    }
383}
384
385impl IntoValue for Value {
386    fn into_value(self) -> libsql::Result<libsql::Value> {
387        Ok(match self {
388            Value::Text(value) => libsql::Value::Text(value),
389            Value::Integer(value) => libsql::Value::Integer(value),
390            Value::Boolean(value) => libsql::Value::Text(value.to_string()),
391            // TODO: Identifier could be a Integer?
392            Value::Identifier(value) => libsql::Value::Integer(value as i64),
393            Value::Blob(value) | Value::Json(value) => libsql::Value::Blob(value),
394            Value::Null => libsql::Value::Null,
395        })
396    }
397}
398
399impl From<libsql::Value> for Value {
400    fn from(value: libsql::Value) -> Self {
401        match value {
402            libsql::Value::Text(value) => Value::Text(value),
403            libsql::Value::Integer(value) => Value::Integer(value),
404            libsql::Value::Null => Value::Null,
405            libsql::Value::Blob(value) => {
406                // TODO: Is this the best way of doing this?
407                if let Some(start) = value.first() {
408                    if *start == b'{' || *start == b'[' {
409                        return Value::Json(value);
410                    }
411                }
412                Value::Blob(value)
413            }
414            libsql::Value::Real(_) => {
415                todo!("Real values are not supported yet")
416            }
417        }
418    }
419}