Skip to content

Commit 49a072d

Browse files
Re-port SQLite to the flat platform
Brings SQLite back online under the new flat-platform architecture (it had been dropped during the zig-compiler migration and only survived as disabled `.todoroc` examples). - platform/InternalSqlite.roc, platform/Sqlite.roc: host-ABI types plus the full decoder/combinator/lifecycle API (prepare!/bind!/columns!/column_value!/ step!/reset! + execute!/query!/query_many!/prepared variants, leaf + nullable decoders, decode_record/map_value/map_value_result, ErrCode/errcode_to_str). The opaque `Stmt :: Box(U64)` handle stores the raw statement pointer. - src/lib.rs: SQLite host fns over libsqlite3-sys (bundled) using the box-payload handle helpers, a per-path connection cache, and value/error marshalling. - Cargo.toml: add libsqlite3-sys = "=0.33.0" (bundled). - examples/sqlite-basic.roc, sqlite-everything.roc (+ todos.db / todos2.db fixtures): migrated from the old syntax; both run green. - ci: run sqlite-basic in the expect suite. Note: the regenerated src/roc_platform_abi.rs requires the upstream RustGlue.roc box-header fix (decref_box_with now takes an explicit payload_contains_refcounted flag instead of inferring it from payload_decref.is_some(); the old inference freed Box(U64) host handles from the wrong base and segfaulted). basic-cli must be regenerated against a roc that includes that fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0b8f3e1 commit 49a072d

15 files changed

Lines changed: 2262 additions & 757 deletions

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ crate-type = ["staticlib"]
1414
crossterm = "=0.29.0"
1515
getrandom = "=0.2.16"
1616
libc = "=0.2.180"
17+
libsqlite3-sys = { version = "=0.33.0", features = ["bundled"] }
1718

1819
[target.'cfg(not(target_os = "macos"))'.dependencies]
1920
sys-locale = "=0.3.2"

ci/all_tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ EXPECT_EXAMPLES=(
129129
"tty"
130130
"dir"
131131
"env-var"
132+
"sqlite-basic"
132133
)
133134

134135
for roc_file in "${EXAMPLES_DIR}"*.roc; do

ci/expect_scripts/sqlite-basic.exp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/expect
2+
3+
# uncomment line below for debugging
4+
# exp_internal 1
5+
6+
set timeout 7
7+
8+
source ./ci/expect_scripts/shared-code.exp
9+
10+
set env(DB_PATH) ./examples/todos.db
11+
12+
spawn $env(EXAMPLES_DIR)sqlite-basic
13+
14+
15+
set expected_output [normalize_output {
16+
All Todos:
17+
id: 3, task: Share my ❤️ for Roc, status: Todo
18+
19+
Completed Todos:
20+
id: 1, task: Prepare for AoC, status: Completed
21+
}]
22+
23+
expect $expected_output {
24+
expect eof {
25+
check_exit_and_segfault
26+
}
27+
}
28+
29+
puts stderr "\nExpect script failed: output was not as expected. Diff the output with expected_output in this script. Alternatively, uncomment `exp_internal 1` to debug."
30+
exit 1

examples/sqlite-basic.roc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
app [main!] { pf: platform "../platform/main.roc" }
2+
3+
import pf.Env
4+
import pf.Stdout
5+
import pf.Sqlite
6+
7+
# To run this example: check the README.md in this folder and set `export DB_PATH=./examples/todos.db`
8+
9+
# Demo of basic Sqlite usage
10+
11+
# Sql to create the table:
12+
# CREATE TABLE todos (
13+
# id INTEGER PRIMARY KEY AUTOINCREMENT,
14+
# task TEXT NOT NULL,
15+
# status TEXT NOT NULL
16+
# );
17+
18+
main! = |_args| {
19+
db_path =
20+
match Env.var!("DB_PATH") {
21+
Ok(p) => p
22+
Err(_) => "./examples/todos.db"
23+
}
24+
25+
todos = query_todos_by_status!(db_path, "todo")?
26+
27+
_h1 = Stdout.line!("All Todos:")
28+
29+
List.for_each!(todos, |todo| print_todo!(todo))
30+
31+
completed_todos = query_todos_by_status!(db_path, "completed")?
32+
33+
_h2 = Stdout.line!("")
34+
_h3 = Stdout.line!("Completed Todos:")
35+
List.for_each!(completed_todos, |todo| print_todo!(todo))
36+
37+
Ok({})
38+
}
39+
40+
Todo : { id : Str, status : TodoStatus, task : Str }
41+
42+
print_todo! = |todo|
43+
match Stdout.line!(" id: ${todo.id}, task: ${todo.task}, status: ${status_to_str(todo.status)}") {
44+
_ => {}
45+
}
46+
47+
query_todos_by_status! = |db_path, status|
48+
Sqlite.query_many!(
49+
{
50+
path: db_path,
51+
query: "SELECT id, task, status FROM todos WHERE status = :status;",
52+
bindings: [{ name: ":status", value: String(status) }],
53+
rows: decode_todo,
54+
},
55+
)
56+
57+
# A row decoder is `List(Str) -> (Stmt => Try(a, err))`; the new compiler does not
58+
# support the record-builder (`<-`) sugar, so we combine the leaf decoders by hand.
59+
decode_todo = |cols|
60+
|stmt| {
61+
id = Sqlite.i64("id")(cols)(stmt)?
62+
task = Sqlite.str("task")(cols)(stmt)?
63+
status_str = Sqlite.str("status")(cols)(stmt)?
64+
status = decode_todo_status(status_str)?
65+
Ok({ id: I64.to_str(id), task, status })
66+
}
67+
68+
TodoStatus : [Todo, Completed, InProgress]
69+
70+
status_to_str : TodoStatus -> Str
71+
status_to_str = |status|
72+
match status {
73+
Todo => "Todo"
74+
Completed => "Completed"
75+
InProgress => "InProgress"
76+
}
77+
78+
decode_todo_status = |status_str|
79+
match status_str {
80+
"todo" => Ok(Todo)
81+
"completed" => Ok(Completed)
82+
"in-progress" => Ok(InProgress)
83+
_ => Err(ParseError("Unknown status str: ${status_str}"))
84+
}

examples/sqlite-basic.todoroc

Lines changed: 0 additions & 74 deletions
This file was deleted.

0 commit comments

Comments
 (0)