Skip to content

Commit

Permalink
Merge pull request #44 from libsql/libsqlcrate
Browse files Browse the repository at this point in the history
local backend: migrate to libsql crate
  • Loading branch information
psarna authored Aug 31, 2023
2 parents e1af376 + 5a96b03 commit f0f9bd2
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 45 deletions.
6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,20 @@ http = { version = "0.2", optional = true }
bytes = { version = "1.4.0", optional = true }
anyhow = "1.0.69"
reqwest = { version = "0.11.14", optional = true, default-features = false, features = ["rustls-tls"] }
rusqlite = { version = "0.28.0", optional = true, default-features = false, features = [
"column_decltype"
] }
hrana-client = { version = "0.3", optional = true }
hrana-client-proto = { version = "0.2" }
futures-util = { version = "0.3.21", optional = true }
serde = "1.0.159"
tracing = "0.1.37"
futures = "0.3.28"
fallible-iterator = "0.2.0"
libsql = { version = "0.1.6", optional = true }

[features]
default = ["local_backend", "hrana_backend", "reqwest_backend", "mapping_names_to_values_in_rows"]
workers_backend = ["worker", "futures-util"]
reqwest_backend = ["reqwest"]
local_backend = ["rusqlite"]
local_backend = ["libsql"]
spin_backend = ["spin-sdk", "http", "bytes"]
hrana_backend = ["hrana-client"]
separate_url_for_queries = []
Expand Down
84 changes: 44 additions & 40 deletions src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,44 @@ use crate::{proto, proto::StmtResult, BatchResult, Col, ResultSet, Statement, Va
use anyhow::Result;
use sqlite3_parser::ast::{Cmd, Stmt};
use sqlite3_parser::lexer::sql::Parser;
use std::sync::{Arc, Mutex};

use fallible_iterator::FallibleIterator;
use rusqlite::types::Value as RusqliteValue;

/// Database client. This is the main structure used to
/// communicate with the database.
#[derive(Debug)]
pub struct Client {
inner: Arc<Mutex<rusqlite::Connection>>,
db: libsql::Database,
conn: libsql::Connection,
}

impl std::fmt::Debug for Client {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("local::Client").finish()
}
}

struct ValueWrapper(Value);

impl From<ValueWrapper> for RusqliteValue {
impl From<ValueWrapper> for libsql::Value {
fn from(v: ValueWrapper) -> Self {
match v.0 {
Value::Null => RusqliteValue::Null,
Value::Integer { value: n } => RusqliteValue::Integer(n),
Value::Text { value: s } => RusqliteValue::Text(s),
Value::Float { value: d } => RusqliteValue::Real(d),
Value::Blob { value: b } => RusqliteValue::Blob(b),
Value::Null => libsql::Value::Null,
Value::Integer { value: n } => libsql::Value::Integer(n),
Value::Text { value: s } => libsql::Value::Text(s),
Value::Float { value: d } => libsql::Value::Real(d),
Value::Blob { value: b } => libsql::Value::Blob(b),
}
}
}

impl From<RusqliteValue> for ValueWrapper {
fn from(v: RusqliteValue) -> Self {
impl From<libsql::Value> for ValueWrapper {
fn from(v: libsql::Value) -> Self {
match v {
RusqliteValue::Null => ValueWrapper(Value::Null),
RusqliteValue::Integer(n) => ValueWrapper(Value::Integer { value: n }),
RusqliteValue::Text(s) => ValueWrapper(Value::Text { value: s }),
RusqliteValue::Real(d) => ValueWrapper(Value::Float { value: d }),
RusqliteValue::Blob(b) => ValueWrapper(Value::Blob { value: b }),
libsql::Value::Null => ValueWrapper(Value::Null),
libsql::Value::Integer(n) => ValueWrapper(Value::Integer { value: n }),
libsql::Value::Text(s) => ValueWrapper(Value::Text { value: s }),
libsql::Value::Real(d) => ValueWrapper(Value::Float { value: d }),
libsql::Value::Blob(b) => ValueWrapper(Value::Blob { value: b }),
}
}
}
Expand All @@ -45,21 +49,17 @@ impl Client {
///
/// # Arguments
/// * `path` - path of the local database
pub fn new(path: impl AsRef<std::path::Path>) -> anyhow::Result<Self> {
Ok(Self {
inner: Arc::new(Mutex::new(
rusqlite::Connection::open(path).map_err(|e| anyhow::anyhow!("{e}"))?,
)),
})
pub fn new(path: impl Into<String>) -> anyhow::Result<Self> {
let db = libsql::Database::open(path.into())?;
let conn = db.connect()?;
Ok(Self { db, conn })
}

/// Establishes a new in-memory database and connects to it.
pub fn in_memory() -> anyhow::Result<Self> {
Ok(Self {
inner: Arc::new(Mutex::new(
rusqlite::Connection::open(":memory:").map_err(|e| anyhow::anyhow!("{e}"))?,
)),
})
let db = libsql::Database::open(":memory:")?;
let conn = db.connect()?;
Ok(Self { db, conn })
}

pub fn from_env() -> anyhow::Result<Self> {
Expand All @@ -73,6 +73,10 @@ impl Client {
Self::new(path)
}

pub async fn sync(&self) -> anyhow::Result<usize> {
self.db.sync().await.map_err(|e| anyhow::anyhow!("{}", e))
}

/// Executes a batch of SQL statements.
/// Each statement is going to run in its own transaction,
/// unless they're wrapped in BEGIN and END
Expand All @@ -98,14 +102,14 @@ impl Client {
for stmt in stmts {
let stmt = stmt.into();
let sql_string = &stmt.sql;
let params = rusqlite::params_from_iter(
stmt.args
.into_iter()
.map(ValueWrapper)
.map(RusqliteValue::from),
);
let inner = self.inner.lock().unwrap();
let mut stmt = inner.prepare(sql_string)?;
let params: libsql::Params = stmt
.args
.into_iter()
.map(ValueWrapper)
.map(libsql::Value::from)
.collect::<Vec<_>>()
.into();
let stmt = self.conn.prepare(sql_string)?;
let cols: Vec<Col> = stmt
.columns()
.into_iter()
Expand All @@ -114,7 +118,7 @@ impl Client {
})
.collect();
let mut rows = Vec::new();
let mut input_rows = match stmt.query(params) {
let input_rows = match stmt.query(&params) {
Ok(rows) => rows,
Err(e) => {
step_results.push(None);
Expand All @@ -126,15 +130,15 @@ impl Client {
};
while let Some(row) = input_rows.next()? {
let cells = (0..cols.len())
.map(|i| ValueWrapper::from(row.get::<usize, RusqliteValue>(i).unwrap()).0)
.map(|i| ValueWrapper::from(row.get_value(i as i32).unwrap()).0)
.collect();
rows.push(cells)
}
let parser = Parser::new(sql_string.as_bytes());
let cmd = parser.last();

let last_insert_rowid = match cmd {
Ok(Some(Cmd::Stmt(Stmt::Insert { .. }))) => Some(inner.last_insert_rowid()),
Ok(Some(Cmd::Stmt(Stmt::Insert { .. }))) => Some(self.conn.last_insert_rowid()),
_ => None,
};

Expand All @@ -143,7 +147,7 @@ impl Client {
Cmd::Stmt(Stmt::Insert { .. })
| Cmd::Stmt(Stmt::Update { .. })
| Cmd::Stmt(Stmt::Delete { .. }),
)) => inner.changes(),
)) => self.conn.changes(),
_ => 0,
};

Expand Down
2 changes: 1 addition & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod tests {
fn test_pop_query_param_not_existing() {
let mut url = Url::parse("http://turso.io/?super=yes&sqld=yo").unwrap();
let param = "ohno".to_string();
let result = pop_query_param(&mut url, param.clone());
let result = pop_query_param(&mut url, param);
assert_eq!(result, None);
assert_eq!(url.as_str(), "http://turso.io/?super=yes&sqld=yo");
}
Expand Down

0 comments on commit f0f9bd2

Please sign in to comment.