# sqlite-mumu
_A MuMu/Lava plugin to provide fast, native SQLite access with typed params, streaming result-sets, and friendly data-last ergonomics._
**Repository:** <https://gitlab.com/tofo/sqlite-mumu>
**License:** MIT OR Apache-2.0
**Engine compatibility:** `core-mumu = 0.9.0-rc.4` (host builds; not intended for wasm)
---
## What this plugin aims to do
- to expose a minimal, predictable set of `sqlite:*` functions to MuMu/Lava
- to interoperate cleanly with MuMu values (typed arrays, keyed arrays/objects, iterators)
- to stream `SELECT` results via the core `Iterator` type (no giant in-memory buffers)
- to bind parameters safely and ergonomically from MuMu arrays (int/float/string/bool/mixed)
- to preserve concrete MuMu types on reads (e.g., integers as `Long`, floats as `Float`)
- to remain “host-only” while exporting a dynamic entrypoint for `extend("sqlite")`
On native builds the plugin registers itself through the exported entrypoint `Cargo_lock(...)` so `extend("sqlite")` can load it at runtime.
---
## Feature overview
### 1) Open / Close
- `sqlite:open([path, flags]) → handle:int`
- `path` — SQLite file path (e.g., `"/tmp/app.db"`; special SQLite URIs are allowed)
- `flags` — `"ro"` (read-only) · `"rw"` (read/write) · `"rwc"` (read/write create; **default**)
- returns an integer **handle** for subsequent calls
- `sqlite:close([handle]) → bool`
- accepts key `"handle"` (and synonyms `"db"`, `"conn"`)
- returns `true` on success; errors if the handle is unknown
Connections are stored internally as `Arc<Mutex<rusqlite::Connection>>`, ensuring safe, serialized access.
### 2) Execute queries
- `sqlite:query([handle, sql, params]) → Iterator | int`
- `sql` — SQL string (or a single-element `StrArray`)
- `params` — optional parameters as:
- `IntArray`, `FloatArray`, `StrArray`, `BoolArray`, or `MixedArray`
- `_` (`Placeholder`) maps to `NULL`
- For `SELECT`/`PRAGMA`/`WITH`:
- returns a MuMu **`Iterator`** of **rows as `KeyedArray`**
(column names preserved as keys; insertion order preserved)
- For `INSERT`/`UPDATE`/`DELETE`/DDL:
- returns an **`Int`** (rows changed) from SQLite’s `execute`
**Type mapping (SQLite → MuMu per column):**
- `NULL` → `Placeholder`
- `INTEGER` → `Long`
- `REAL` → `Float`
- `TEXT` → `SingleString`
- `BLOB` → `SingleString("[BLOB]")` (opaque marker)
**Type mapping (MuMu params → SQLite):**
- `Int`/`Long`/`IntArray` → integer binds
- `Float`/`FloatArray` → real binds
- `SingleString`/`StrArray` → text binds
- `Bool`/`BoolArray` → `1`/`0` integer binds
- `MixedArray` — element-wise mapping based on the above
- `Placeholder` → `NULL`
**Streaming:** result rows are yielded on demand. Consume with any iterator-aware tool (e.g., `slog`, your own collectors, or array/flow helpers).
---
## API surface
All functions use keyed arrays for clarity; handle key synonyms are accepted (`handle`, `db`, or `conn`).
- `sqlite:open([path:"/tmp/app.db", flags:"rwc"]) → 1000`
- `sqlite:query([handle:1000, sql:"SELECT 1 AS n", params:_]) → Iterator`
- `sqlite:close([db:1000]) → true`
Error messages are explicit (e.g., `"sqlite:query: prepare error: ..."`, `"sqlite:open: 'path' field missing"`). When the MuMu interpreter runs with `--verbose` (or `LAVA_VERBOSE=1`), the plugin prints extra diagnostics to `stderr` (e.g., row streaming traces).
---
## Minimal examples
```mu
extend("sqlite")
h = sqlite:open([path:"/tmp/example.db", flags:"rwc"])
// DDL / DML return changed-row counts
sqlite:query([handle:h, sql:"CREATE TABLE IF NOT EXISTS t(id INTEGER PRIMARY KEY, name TEXT)"])
sqlite:query([handle:h, sql:"INSERT INTO t(name) VALUES (?)", params:["Ada"]])
sqlite:query([handle:h, sql:"INSERT INTO t(name) VALUES (?)", params:["Ben"]])
// SELECT returns an Iterator of KeyedArray rows
rows = sqlite:query([handle:h, sql:"SELECT id, name FROM t WHERE id > ?", params:[0]])
// Stream to console (one row per poll tick)
slog(rows)
// Clean up
sqlite:close([handle:h])
```
```mu
// Parameter typing: arrays map directly to SQLite binds
h = sqlite:open([path:"/tmp/example.db"])
sqlite:query([handle:h, sql:"UPDATE t SET name = ? WHERE id = ?", params:["Eve", 1]])
sqlite:close([handle:h])
```
---
## Return & error conventions
- `sqlite:open` — returns `Int` handle; errors on invalid flags/path/open failure
- `sqlite:query`
- `SELECT`/`PRAGMA`/`WITH` — returns `Iterator` of `KeyedArray` rows
- other statements — returns `Int` rows changed
- parameters must be representable as SQLite binds; otherwise a descriptive error is returned
- `sqlite:close` — returns `Bool(true)` if the handle existed, errors otherwise
`Iterator` exhaustion signals `"NO_MORE_DATA"` internally; helper functions (e.g., `slog`) handle this.
---
## Design notes
- **Host-only:** Uses `rusqlite`; not intended for wasm.
- **Streaming first:** `SELECT` results are not fully materialized.
- **Typed columns:** preserves integer/real distinction (`Long` vs `Float`).
- **Stable key order:** rows are `KeyedArray` (insertion-order map).
- **Diagnostics:** verbose logging is gated by the interpreter’s verbosity.
- **Platform:** on Windows the build enables a bundled SQLite (`libsqlite3-sys` with `features = ["bundled"]`); on Unix-likes it uses the system library by default.
---
## Contributing
Issues and merge requests welcome at:
<https://gitlab.com/tofo/sqlite-mumu>
Please align changes with the MuMu core conventions:
- keyed-argument APIs
- predictable type mapping (columns & params)
- iterator streaming for result sets
- clear, actionable error messages
---
## License
Licensed under either of:
- MIT license
- Apache License, Version 2.0
at your option.