Skip to content

Commit cb4f404

Browse files
committed
chore: Using MemoryStorage in a WASM environment
1 parent 76a5349 commit cb4f404

File tree

16 files changed

+532
-77
lines changed

16 files changed

+532
-77
lines changed

.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.wasm32-unknown-unknown]
2+
rustflags = ["--cfg=getrandom_backend=\"wasm_js\""]

Cargo.lock

Lines changed: 20 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ required-features = ["net"]
2121

2222
[lib]
2323
doctest = false
24+
crate-type = ["cdylib", "rlib"]
2425

2526
[features]
2627
default = ["macros"]
@@ -40,7 +41,7 @@ bincode = { version = "1" }
4041
bumpalo = { version = "3", features = ["allocator-api2", "collections", "std"] }
4142
byteorder = { version = "1" }
4243
chrono = { version = "0.4" }
43-
comfy-table = { version = "7" }
44+
comfy-table = { version = "7", default-features = false }
4445
csv = { version = "1" }
4546
fixedbitset = { version = "0.4" }
4647
itertools = { version = "0.12" }
@@ -50,7 +51,6 @@ parking_lot = { version = "0.12", features = ["arc_lock"] }
5051
petgraph = { version = "0.6" }
5152
recursive = { version = "0.1" }
5253
regex = { version = "1" }
53-
rocksdb = { version = "0.23" }
5454
rust_decimal = { version = "1" }
5555
serde = { version = "1", features = ["derive", "rc"] }
5656
kite_sql_serde_macros = { version = "0.1.0", path = "kite_sql_serde_macros" }
@@ -60,7 +60,6 @@ thiserror = { version = "1" }
6060
typetag = { version = "0.2" }
6161
ulid = { version = "1", features = ["serde"] }
6262
genawaiter = { version = "0.99" }
63-
rand = { version = "0.8" }
6463

6564
# Feature: net
6665
async-trait = { version = "0.1", optional = true }
@@ -84,11 +83,20 @@ pprof = { version = "0.13", features = ["flamegraph", "criterion"] }
8483

8584
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
8685
dirs = { version = "5" }
86+
rocksdb = { version = "0.23" }
8787

8888
[target.'cfg(target_arch = "wasm32")'.dependencies]
89-
wasm-bindgen = { version = "0.2" }
90-
web-sys = { version = "0.3", features = ["Storage", "Window"] }
91-
base64 = { version = "0.21" }
89+
wasm-bindgen = { version = "0.2" }
90+
web-sys = { version = "0.3", features = [
91+
"Storage",
92+
"Window",
93+
] }
94+
base64 = { version = "0.21" }
95+
getrandom = { version = "0.2", features = ["js"] }
96+
getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] }
97+
js-sys = { version = "0.3" }
98+
serde-wasm-bindgen = { version = "0.6" }
99+
once_cell = { version = "1" }
92100

93101
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
94102
wasm-bindgen-test = "0.3"
@@ -102,3 +110,6 @@ members = [
102110

103111
[profile.release]
104112
lto = true
113+
114+
[package.metadata.wasm-pack.profile.release]
115+
wasm-opt = false

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,24 @@
3434
- All metadata and actual data in KV Storage, and there is no state component (e.g. system table) in the middle
3535
- Supports extending storage for customized workloads
3636
- Supports most of the SQL 2016 syntax
37+
- Ships a WebAssembly build for JavaScript runtimes
3738

3839
#### 👉[check more](docs/features.md)
3940

41+
## WebAssembly
42+
- Build: `wasm-pack build --release --target nodejs` (outputs to `./pkg`; use `--target web` or `--target bundler` for browser/bundler setups).
43+
- Usage:
44+
```js
45+
import { WasmDatabase } from "./pkg/kite_sql.js";
46+
47+
const db = new WasmDatabase();
48+
await db.execute("create table demo(id int primary key, v int)");
49+
await db.execute("insert into demo values (1, 2), (2, 4)");
50+
const rows = db.run("select * from demo").rows();
51+
console.log(rows.map((r) => r.values.map((v) => v.Int32 ?? v)));
52+
```
53+
- In Node.js, provide a small `localStorage` shim if you enable statistics-related features (see `examples/wasm_index_usage.test.mjs`).
54+
4055
## Examples
4156
4257
```rust

examples/hello_world.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,48 @@ implement_from_tuple!(
2828
fn main() -> Result<(), DatabaseError> {
2929
let database = DataBaseBuilder::path("./hello_world").build()?;
3030

31+
// 1) Create table and insert multiple rows with mixed types.
3132
database
32-
.run("create table if not exists my_struct (c1 int primary key, c2 int)")?
33+
.run(
34+
"create table if not exists my_struct (
35+
c1 int primary key,
36+
c2 varchar,
37+
c3 int
38+
)",
39+
)?
3340
.done()?;
3441
database
35-
.run("insert into my_struct values(0, 0), (1, 1)")?
42+
.run(
43+
r#"
44+
insert into my_struct values
45+
(0, 'zero', 0),
46+
(1, 'one', 1),
47+
(2, 'two', 2)
48+
"#,
49+
)?
3650
.done()?;
3751

52+
// 2) Update and delete demo.
53+
database
54+
.run("update my_struct set c3 = c3 + 10 where c1 = 1")?
55+
.done()?;
56+
database.run("delete from my_struct where c1 = 2")?.done()?;
57+
58+
// 3) Query and deserialize into Rust struct.
3859
let iter = database.run("select * from my_struct")?;
3960
let schema = iter.schema().clone();
4061

4162
for tuple in iter {
4263
println!("{:?}", MyStruct::from((&schema, tuple?)));
4364
}
65+
66+
// 4) Aggregate example.
67+
let mut agg = database.run("select count(*) from my_struct")?;
68+
if let Some(count_row) = agg.next() {
69+
println!("row count = {:?}", count_row?);
70+
}
71+
agg.done()?;
72+
4473
database.run("drop table my_struct")?.done()?;
4574

4675
Ok(())

examples/transaction.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ fn main() -> Result<(), DatabaseError> {
55
let database = DataBaseBuilder::path("./transaction").build_optimistic()?;
66
let mut transaction = database.new_transaction()?;
77

8+
// Scenario: create table and insert rows inside a transaction; visible after commit.
89
transaction
910
.run("create table if not exists t1 (c1 int primary key, c2 int)")?
1011
.done()?;
@@ -18,6 +19,21 @@ fn main() -> Result<(), DatabaseError> {
1819

1920
assert!(database.run("select * from t1").is_ok());
2021

22+
// Scenario: another transaction updates but does not commit; changes stay invisible.
23+
let mut tx2 = database.new_transaction()?;
24+
tx2.run("update t1 set c2 = 99 where c1 = 0")?.done()?;
25+
assert_eq!(
26+
database
27+
.run("select c2 from t1 where c1 = 0")?
28+
.next()
29+
.unwrap()?
30+
.values[0]
31+
.i32(),
32+
Some(0)
33+
);
34+
// rollback
35+
drop(tx2);
36+
2137
database.run("drop table t1")?.done()?;
2238

2339
Ok(())

examples/wasm_hello_world.test.mjs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Simple wasm smoke test; run `wasm-pack build --target nodejs` first to generate ./pkg
2+
import assert from "node:assert/strict";
3+
import { createRequire } from "module";
4+
5+
const require = createRequire(import.meta.url);
6+
const { WasmDatabase } = require("../pkg/kite_sql.js");
7+
8+
async function main() {
9+
const db = new WasmDatabase();
10+
11+
await db.execute("drop table if exists my_struct");
12+
await db.execute("create table my_struct (c1 int primary key, c2 int)");
13+
await db.execute("insert into my_struct values(0, 0), (1, 1)");
14+
15+
const rows = db.run("select * from my_struct").rows();
16+
assert.equal(rows.length, 2, "should return two rows");
17+
18+
const [first, second] = rows;
19+
const firstVals = first.values.map((v) => v.Int32 ?? v);
20+
const secondVals = second.values.map((v) => v.Int32 ?? v);
21+
assert.deepEqual(firstVals, [0, 0]);
22+
assert.deepEqual(secondVals, [1, 1]);
23+
24+
await db.execute("update my_struct set c2 = c2 + 10 where c1 = 1");
25+
const after = db.run("select c2 from my_struct where c1 = 1").rows();
26+
assert.deepEqual(after[0].values.map((v) => v.Int32 ?? v), [11]);
27+
28+
// Stream the rows using the iterator interface
29+
const stream = db.run("select * from my_struct");
30+
const streamed = [];
31+
let row = stream.next();
32+
while (row !== undefined) {
33+
streamed.push(row.values.map((v) => v.Int32 ?? v));
34+
row = stream.next();
35+
}
36+
stream.finish();
37+
assert.deepEqual(streamed, [
38+
[0, 0],
39+
[1, 11],
40+
]);
41+
42+
await db.execute("drop table my_struct");
43+
console.log("wasm hello_world test passed");
44+
}
45+
46+
main().catch((err) => {
47+
console.error(err);
48+
process.exit(1);
49+
});

0 commit comments

Comments
 (0)