Skip to content

Commit

Permalink
Add sqlite support
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Levick <ryan.levick@fermyon.com>
  • Loading branch information
rylev committed Jun 22, 2023
1 parent 6fa59f0 commit ff2be96
Show file tree
Hide file tree
Showing 11 changed files with 1,551 additions and 57 deletions.
104 changes: 49 additions & 55 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/spin-js-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ quickjs-wasm-rs = "0.1.4"
quickjs-wasm-sys = "0.1.2"
once_cell = "1.4.0"
serde_json = "1.0.87"
spin-sdk = { git = "https://github.com/fermyon/spin", default-features = false }
spin-sdk = { git = "https://github.com/fermyon/spin", rev = "d092cf40582a707066768737f13ad0b9b8c5cff3", default-features = false, features=["experimental"] }
wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "dde4694aaa6acf9370206527a798ac4ba6a8c5b8" }
rand = "0.8.5"
serde = "1.0.137"
Expand Down
90 changes: 89 additions & 1 deletion crates/spin-js-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use {
key_value::Store,
mysql, outbound_http, pg,
redis::{self, RedisResult},
sqlite,
},
std::{
collections::HashMap,
Expand Down Expand Up @@ -696,6 +697,88 @@ fn open_kv(context: &Context, _this: &Value, args: &[Value]) -> Result<Value> {
}
}

fn open_sqlite(context: &Context, _this: &Value, args: &[Value]) -> Result<Value> {
let implementation = match args {
[] => sqlite::Connection::open_default()?,
[name] => sqlite::Connection::open(&deserialize_helper(name)?)?,
_ => bail!("expected one argument (name) got {} arguments", args.len()),
};
let implementation = Rc::new(implementation);
let connection = context.object_value()?;

connection.set_property(
"execute",
context.wrap_callback({
let implementation = implementation.clone();
move |context, _this: &Value, args: &[Value]| {
let (query, js_parameters) = match args {
[js_query] => (deserialize_helper(js_query)?, Vec::new()),
[js_query, js_parameters, ..] => {
let mut props = js_parameters.properties()?;
let mut parameters = Vec::new();
while let Some(_) = props.next_key()? {
parameters.push(props.next_value()?);
}

(deserialize_helper(js_query)?, parameters)
}
[] => bail!("expected arguments to the `query` function but got none"),
};
let parameters = js_parameters
.iter()
.map(|v| {
let p = if v.is_null() {
sqlite::ValueParam::Null
} else if v.is_repr_as_i32() {
let deserializer = &mut Deserializer::from(v.clone());
sqlite::ValueParam::Integer(i64::deserialize(deserializer)?)
} else if let Ok(s) = v.as_str() {
sqlite::ValueParam::Text(s)
} else if let Ok(s) = v.as_bytes() {
sqlite::ValueParam::Blob(s)
} else if let Ok(f) = v.as_f64() {
sqlite::ValueParam::Real(f)
} else {
bail!("invalid argument type for `parameters` argument to `execute` function: {:?}", v)
};
Ok(p)
})
.collect::<anyhow::Result<Vec<_>>>()?;
let result = implementation.execute(&query, &parameters)?;

let mut serializer = Serializer::from_context(context)?;
let columns = result.columns;
columns.serialize(&mut serializer)?;
let js_columns = serializer.value;

let js_rows = context.array_value()?;
for row in result.rows {
let js_row = context.array_value()?;
for value in row.values {
let js_value = match value {
sqlite::ValueResult::Null => context.null_value()?,
sqlite::ValueResult::Integer(i) => context.value_from_i64(i)?,
sqlite::ValueResult::Real(r) => context.value_from_f64(r)?,
sqlite::ValueResult::Text(s) => context.value_from_str(&s)?,
sqlite::ValueResult::Blob(b) => context.array_buffer_value(&b)?,
};
js_row.append_property(js_value)?;
}
js_rows.append_property(js_row)?;
}

let result = context.object_value()?;
result.set_property("columns", js_columns)?;
result.set_property("rows", js_rows)?;

Ok(result)
}
})?,
)?;

Ok(connection)
}

enum RdbmsParameter {
Boolean(bool),
Int32(i32),
Expand Down Expand Up @@ -1035,13 +1118,18 @@ fn do_init() -> Result<()> {
kv.set_property("open", context.wrap_callback(open_kv)?)?;
kv.set_property("openDefault", context.wrap_callback(open_kv)?)?;

let sqlite = context.object_value()?;
sqlite.set_property("open", context.wrap_callback(open_sqlite)?)?;
sqlite.set_property("openDefault", context.wrap_callback(open_sqlite)?)?;

let spin_sdk = context.object_value()?;
spin_sdk.set_property("config", config)?;
spin_sdk.set_property("http", http)?;
spin_sdk.set_property("redis", redis)?;
spin_sdk.set_property("mysql", mysql)?;
spin_sdk.set_property("pg", postgres)?;
spin_sdk.set_property("kv", kv)?;
spin_sdk.set_property("sqlite", sqlite)?;

let _glob = context.object_value()?;
_glob.set_property("get", context.wrap_callback(get_glob)?)?;
Expand Down Expand Up @@ -1188,6 +1276,6 @@ fn deserialize_helper(value: &Value) -> Result<String> {
let result = String::deserialize(deserializer);
match result {
Ok(value) => Ok(value),
_ => bail!("failed to deserialize string"),
_ => bail!("failed to deserialize value '{value:?}' as string"),
}
}
4 changes: 4 additions & 0 deletions examples/javascript/sqlite/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
dist
target
.spin/
11 changes: 11 additions & 0 deletions examples/javascript/sqlite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SQLite

This is a simple showcase of the SQLite feature in the Spin JS SDK.

## Running

Run the following:

```bash
spin build -u --sqlite @migration.sql
```
7 changes: 7 additions & 0 deletions examples/javascript/sqlite/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
due_date DATE,
starred BOOLEAN DEFAULT 0,
is_completed BOOLEAN DEFAULT 0
);
Loading

0 comments on commit ff2be96

Please sign in to comment.