Skip to content

Commit 102d628

Browse files
committed
docs: Add SQLite compiletime setup guide and helper snippets
1 parent 4b3eb2b commit 102d628

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

WURST_SQLITE_GUIDE.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Wurst SQLite Compiletime Support Guide
2+
3+
This guide covers setting up an SQLite database locally alongside Wurst scripts, allowing you to load and persist structured data during Wurst compilation using the new SQLite JDBC bindings.
4+
5+
### 1. Database Setup / Initialization
6+
You don’t strictly *need* an initial physical file if you use `:memory:` or execute table creation directly through Wurst. However, best practice for map development is to use a dedicated standalone file inside your Wurst project (for example, `wurst_data.db`).
7+
8+
If you'd rather not manually create the `.db` file using `sqlite3`, you can rely on Wurst to generate the target `.db` file and its tables during `@compiletime`! SQLite JDBC will automatically create the file if it does not exist when using `sqlite_open()`.
9+
10+
***
11+
12+
### 2. Full Test Snippet: Wurst Script
13+
Save this code in your project (e.g. `SQLiteIntegrationTest.wurst`). This snippet demonstrates creating the database, loading dummy values into it using pure `INSERT` statements, updating it, and executing `sqlite_select` (with assertions) to ensure everything behaves identically during compiletime and testing.
14+
15+
> **Note on Persisted Databases:**
16+
> When testing with physical `.db` files across multiple builds, use `DROP TABLE IF EXISTS` before `CREATE TABLE` to ensure your build scripts remain idempotent.
17+
18+
```wurst
19+
package SQLiteIntegrationTest
20+
import LinkedList
21+
import ErrorHandling
22+
23+
// Natively exposed bindings
24+
@extern native sqlite_open(string path) returns int
25+
@extern native sqlite_prepare(int conn, string q) returns int
26+
@extern native sqlite_step(int stmt) returns boolean
27+
@extern native sqlite_column_string(int stmt, int idx) returns string
28+
@extern native sqlite_column_count(int stmt) returns int
29+
@extern native sqlite_exec(int conn, string q)
30+
@extern native sqlite_finalize(int stmt)
31+
@extern native sqlite_close(int conn)
32+
33+
// The generalized result class with variable binding
34+
public class SqlResult
35+
string v1 = ""
36+
string v2 = ""
37+
string v3 = ""
38+
string v4 = ""
39+
// Add up to v30 as needed
40+
41+
// A full SQL-Select helper
42+
public function sqlite_select(int db, string query) returns LinkedList<SqlResult>
43+
let list = new LinkedList<SqlResult>()
44+
let stmt = sqlite_prepare(db, query)
45+
let cols = sqlite_column_count(stmt)
46+
47+
while sqlite_step(stmt)
48+
let row = new SqlResult()
49+
if cols > 0
50+
row.v1 = sqlite_column_string(stmt, 0)
51+
if cols > 1
52+
row.v2 = sqlite_column_string(stmt, 1)
53+
if cols > 2
54+
row.v3 = sqlite_column_string(stmt, 2)
55+
if cols > 3
56+
row.v4 = sqlite_column_string(stmt, 3)
57+
// Map more bindings if expanding SqlResult
58+
list.add(row)
59+
60+
sqlite_finalize(stmt)
61+
return list
62+
63+
// ============================================
64+
// Database Tests & Population Functions
65+
// ============================================
66+
67+
function buildAndVerifyDatabase()
68+
// Open a database connection (Use path like "heroes.db" for persistent storage)
69+
// We use :memory: here for rapid temporary testing
70+
let db = sqlite_open(":memory:")
71+
72+
// 1. Create table structured schema
73+
sqlite_exec(db, "DROP TABLE IF EXISTS Heroes")
74+
sqlite_exec(db, "CREATE TABLE Heroes (id INTEGER PRIMARY KEY, name TEXT, role TEXT, power_level INTEGER)")
75+
76+
// 2. Insert dummy data
77+
sqlite_exec(db, "INSERT INTO Heroes (name, role, power_level) VALUES ('Arthur', 'Paladin', 9000)")
78+
sqlite_exec(db, "INSERT INTO Heroes (name, role, power_level) VALUES ('Merlin', 'Mage', 8500)")
79+
sqlite_exec(db, "INSERT INTO Heroes (name, role, power_level) VALUES ('Robin', 'Archer', 7200)")
80+
81+
// 3. Execute select to read all fields
82+
let results = sqlite_select(db, "SELECT name, role, power_level FROM Heroes ORDER BY power_level DESC")
83+
84+
// 4. Verify length matches insertions
85+
if results.size() != 3
86+
error("Expected 3 database entries, found " + results.size().toString())
87+
88+
// 5. Verify the highest power is returned first correctly (Arthur is 9000 -> v3)
89+
let topHero = results.get(0)
90+
if topHero.v1 != "Arthur" or topHero.v3 != "9000"
91+
error("Expected Arthur as highest power, but got " + topHero.v1 + " with " + topHero.v3)
92+
93+
// 5b. Verify standard fetch
94+
let mageHero = results.get(1)
95+
if mageHero.v1 != "Merlin" or mageHero.v2 != "Mage"
96+
error("Validation Failed for Merlin row!")
97+
98+
// 6. Output to the developer console log
99+
print("Database built and successfully validated all rows!")
100+
101+
// Close the connection
102+
sqlite_close(db)
103+
104+
// Executes inside Wurst Unit Test run
105+
@test function databaseSystemTest()
106+
buildAndVerifyDatabase()
107+
108+
// Executes during Build or IDE evaluation
109+
@compiletime function compilerDatabaseLoad()
110+
buildAndVerifyDatabase()
111+
```
112+
113+
### 3. Running & Verifying
114+
If you drop the file above into your repo, you can immediately test it utilizing VSCode:
115+
1. Open the file in the editor.
116+
2. Click the `Run Test` CodeLens helper right above `@test function databaseSystemTest()`
117+
3. In VSCode's Output/Wurst terminal you should see: *"Database built and successfully validated all rows!"* with a green checkmark indicating successful unit execution.
118+
4. If you intentionally sabotage an assertion (e.g., checking if Arthur's power level was 5000), you'll see a red underline directly in VSCode exactly where the query assertion or extraction fails.

0 commit comments

Comments
 (0)