Skip to content

Commit b5dec68

Browse files
authored
more expansive sqlite example (#356)
* more expansive sqlite example * minor improvements
1 parent 7bc8cf9 commit b5dec68

File tree

5 files changed

+249
-5
lines changed

5 files changed

+249
-5
lines changed

ci/all_tests.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ for roc_file in $EXAMPLES_DIR*.roc; do
103103
cd ..
104104
elif [ "$base_file" == "sqlite-basic.roc" ]; then
105105
DB_PATH=${EXAMPLES_DIR}todos.db $ROC dev $roc_file $ROC_BUILD_FLAGS
106+
elif [ "$base_file" == "sqlite-everything.roc" ]; then
107+
DB_PATH=${EXAMPLES_DIR}todos.db $ROC dev $roc_file $ROC_BUILD_FLAGS
106108
elif [ "$base_file" == "temp-dir.roc" ]; then
107109
$ROC dev $roc_file $ROC_BUILD_FLAGS --linker=legacy
108110
elif [ "$base_file" == "file-accessed-modified-created-time.roc" ] && [ "$IS_MUSL" == "1" ]; then
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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) $env(EXAMPLES_DIR)todos.db
11+
12+
spawn $env(EXAMPLES_DIR)sqlite-everything
13+
14+
expect "All Todos:" {
15+
expect "\tid: 1, task: Prepare for AoC, status: Completed" {
16+
expect "\tid: 2, task: Win all the Stars!, status: InProgress" {
17+
expect "\tid: 3, task: Share my ❤️ for Roc, status: Todo" {
18+
expect "In-progress Todos:" {
19+
expect "\tIn-progress tasks: Win all the Stars!" {
20+
expect "Todos sorted by length of task description:" {
21+
expect "\t task: Prepare for AoC, status: Completed" {
22+
expect "\t task: Win all the Stars!, status: InProgress" {
23+
expect "\t task: Share my ❤️ for Roc, status: Todo" {
24+
expect eof {
25+
check_exit_and_segfault
26+
}
27+
}
28+
}
29+
}
30+
}
31+
}
32+
}
33+
}
34+
}
35+
}
36+
}
37+
38+
puts stderr "\nExpect failed: output was different from expected value."
39+
exit 1

examples/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ result
2626
stdin
2727
stdin-basic
2828
sqlite-basic
29+
sqlite-everything
2930
task-list
3031
tcp-client
3132
time

examples/sqlite-basic.roc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ main! : List Arg => Result {} _
2020
main! = |_args|
2121
db_path = Env.var!("DB_PATH")?
2222

23-
todo = query_todos_by_status!(db_path, "todo")?
23+
todos = query_todos_by_status!(db_path, "todo")?
2424

2525
Stdout.line!("All Todos:")?
2626
2727
# print todos
2828
List.for_each_try!(
29-
todo,
29+
todos,
3030
|{ id, task, status }|
3131
Stdout.line!("\tid: ${id}, task: ${task}, status: ${Inspect.to_str(status)}"),
3232
)?
@@ -54,15 +54,15 @@ query_todos_by_status! = |db_path, status|
5454
rows: { Sqlite.decode_record <-
5555
id: Sqlite.i64("id") |> Sqlite.map_value(Num.to_str),
5656
task: Sqlite.str("task"),
57-
status: Sqlite.str("status") |> Sqlite.map_value_result(decode_db_status),
57+
status: Sqlite.str("status") |> Sqlite.map_value_result(decode_todo_status),
5858
},
5959
},
6060
)
6161

6262
TodoStatus : [Todo, Completed, InProgress]
6363

64-
decode_db_status : Str -> Result TodoStatus _
65-
decode_db_status = |status_str|
64+
decode_todo_status : Str -> Result TodoStatus _
65+
decode_todo_status = |status_str|
6666
when status_str is
6767
"todo" -> Ok(Todo)
6868
"completed" -> Ok(Completed)

examples/sqlite-everything.roc

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
app [main!] { pf: platform "../platform/main.roc" }
2+
3+
import pf.Env
4+
import pf.Stdout
5+
import pf.Sqlite
6+
import pf.Arg exposing [Arg]
7+
8+
# To run this example: check the README.md in this folder and set `export DB_PATH=./examples/todos.db`
9+
10+
# Demo of basic Sqlite usage
11+
12+
# Sql that was used to create the table:
13+
# CREATE TABLE todos (
14+
# id INTEGER PRIMARY KEY AUTOINCREMENT,
15+
# task TEXT NOT NULL,
16+
# status TEXT NOT NULL
17+
# );
18+
19+
main! : List Arg => Result {} _
20+
main! = |_args|
21+
db_path = Env.var!("DB_PATH")?
22+
23+
# TODO demo create table, demo nullable field
24+
25+
# Example: print all rows
26+
27+
all_todos = Sqlite.query_many!({
28+
path: db_path,
29+
query: "SELECT * FROM todos;",
30+
bindings: [],
31+
# This uses the record builder syntax: https://www.roc-lang.org/examples/RecordBuilder/README.html
32+
rows: { Sqlite.decode_record <-
33+
id: Sqlite.i64("id"),
34+
task: Sqlite.str("task"),
35+
status: Sqlite.str("status") |> Sqlite.map_value_result(decode_status),
36+
},
37+
})?
38+
39+
Stdout.line!("All Todos:")?
40+
41+
List.for_each_try!(
42+
all_todos,
43+
|{ id, task, status }|
44+
Stdout.line!("\tid: ${Num.to_str(id)}, task: ${task}, status: ${Inspect.to_str(status)}"),
45+
)?
46+
47+
# Example: filter rows by status
48+
49+
tasks_in_progress = Sqlite.query_many!(
50+
{
51+
path: db_path,
52+
query: "SELECT id, task, status FROM todos WHERE status = :status;",
53+
bindings: [{ name: ":status", value: encode_status(InProgress) }],
54+
rows: Sqlite.str("task")
55+
},
56+
)?
57+
58+
Stdout.line!("\nIn-progress Todos:")?
59+
60+
List.for_each_try!(
61+
tasks_in_progress,
62+
|task_description|
63+
Stdout.line!("\tIn-progress tasks: ${task_description}"),
64+
)?
65+
66+
# Example: insert a row
67+
68+
Sqlite.execute!({
69+
path: db_path,
70+
query: "INSERT INTO todos (task, status) VALUES (:task, :status);",
71+
bindings: [
72+
{ name: ":task", value: String("Make sql example.") },
73+
{ name: ":status", value: encode_status(InProgress) },
74+
],
75+
})?
76+
77+
# Example: insert multiple rows from a Roc list
78+
79+
todos_list : List ({task : Str, status : TodoStatus})
80+
todos_list = [
81+
{ task: "Insert Roc list 1", status: Todo },
82+
{ task: "Insert Roc list 2", status: Todo },
83+
{ task: "Insert Roc list 3", status: Todo },
84+
]
85+
86+
values_str =
87+
todos_list
88+
|> List.map_with_index(
89+
|_, indx|
90+
indx_str = Num.to_str(indx)
91+
"(:task${indx_str}, :status${indx_str})",
92+
)
93+
|> Str.join_with(", ")
94+
95+
all_bindings =
96+
todos_list
97+
|> List.map_with_index(
98+
|{ task, status }, indx|
99+
indx_str = Num.to_str(indx)
100+
[
101+
{ name: ":task${indx_str}", value: String(task) },
102+
{ name: ":status${indx_str}", value: encode_status(status) },
103+
],
104+
)
105+
|> List.join
106+
107+
Sqlite.execute!({
108+
path: db_path,
109+
query: "INSERT INTO todos (task, status) VALUES ${values_str};",
110+
bindings: all_bindings,
111+
})?
112+
113+
# Example: update a row
114+
115+
Sqlite.execute!({
116+
path: db_path,
117+
query: "UPDATE todos SET status = :status WHERE task = :task;",
118+
bindings: [
119+
{ name: ":task", value: String("Make sql example.") },
120+
{ name: ":status", value: encode_status(Completed) },
121+
],
122+
})?
123+
124+
# Example: delete a row
125+
126+
Sqlite.execute!({
127+
path: db_path,
128+
query: "DELETE FROM todos WHERE task = :task;",
129+
bindings: [
130+
{ name: ":task", value: String("Make sql example.") },
131+
],
132+
})?
133+
134+
# Example: delete all rows where ID is greater than 3
135+
136+
Sqlite.execute!({
137+
path: db_path,
138+
query: "DELETE FROM todos WHERE id > :id;",
139+
bindings: [
140+
{ name: ":id", value: Integer(3) },
141+
],
142+
})?
143+
144+
# Example: count the number of rows
145+
146+
count = Sqlite.query!({
147+
path: db_path,
148+
query: "SELECT COUNT(*) as \"count\" FROM todos;",
149+
bindings: [],
150+
row: Sqlite.u64("count"),
151+
})?
152+
153+
expect count == 3
154+
155+
# Example: prepared statements
156+
# Note: This leads to better performance if you are executing the same prepared statement multiple times.
157+
158+
prepared_query = Sqlite.prepare!({
159+
path : db_path,
160+
query : "SELECT * FROM todos ORDER BY LENGTH(task);", # sort by the length of the task description
161+
})?
162+
163+
todos_sorted = Sqlite.query_many_prepared!({
164+
stmt: prepared_query,
165+
bindings: [],
166+
rows: { Sqlite.decode_record <-
167+
task: Sqlite.str("task"),
168+
status: Sqlite.str("status") |> Sqlite.map_value_result(decode_status),
169+
},
170+
})?
171+
172+
Stdout.line!("\nTodos sorted by length of task description:")?
173+
174+
List.for_each_try!(
175+
todos_sorted,
176+
|{ task, status }|
177+
Stdout.line!("\t task: ${task}, status: ${Inspect.to_str(status)}"),
178+
)?
179+
180+
Ok({})
181+
182+
TodoStatus : [Todo, Completed, InProgress]
183+
184+
decode_status : Str -> Result TodoStatus _
185+
decode_status = |status_str|
186+
when status_str is
187+
"todo" -> Ok(Todo)
188+
"completed" -> Ok(Completed)
189+
"in-progress" -> Ok(InProgress)
190+
_ -> Err(ParseError("Unknown status str: ${status_str}"))
191+
192+
status_to_str : TodoStatus -> Str
193+
status_to_str = |status|
194+
when status is
195+
Todo -> "todo"
196+
Completed -> "completed"
197+
InProgress -> "in-progress"
198+
199+
200+
encode_status : TodoStatus -> [String Str]
201+
encode_status = |status|
202+
String(status_to_str(status))

0 commit comments

Comments
 (0)