Repository navigation
Description
Follow-up to #432. PR #461 added the Rust cursor API on v3. This issue adds a matching cursor object to every binding, so 3.0.0 is the last release that binding users have to change for incremental results.
What landed in core (v3 @ 1e4386d)
pub fn Minigraf::query(&self, datalog: &str) -> Result<Cursor, MinigrafError>;
pub fn PreparedQuery::query(&self, binds: &[(&str, BindValue)]) -> Result<Cursor, MinigrafError>;
impl Cursor {
pub fn vars(&self) -> &[String];
pub fn next_batch(&mut self, max_rows: usize) -> Result<Option<Batch>, MinigrafError>;
pub fn close(self);
}
impl Iterator for Cursor { type Item = Result<Vec<Value>, MinigrafError>; }Cursoris owned andSend. It borrows nothing from the handle and can outlive it.- The answer is fixed when the cursor opens.
- A batch is never empty,
Nonemeans the end, andmax_rows == 0counts as 1. transact,retractandrulegiveAPI-012. A query with bind slots givesAPI-010.- For now the engine computes the answer when the cursor opens. Streaming operators replace it in 3.x without changing the API.
Design: docs/superpowers/specs/2026-10-07-cursor-api-design.md.
Binding shape
uniffi cannot carry an iterator, so every binding exposes the same object, with a native iterator on top:
db.query(datalog) -> CursorCursor.vars() -> list of stringsCursor.next_batch(n) -> rows, or nothing at the end. Rows use the binding's existing result encoding: the JSON rowsexecute()returns today, or native values where the binding already decodes them.Cursor.close()- An idiomatic iterator over
next_batch: Python__iter__and a context manager, a JS async/sync iterator, JavaIterator+AutoCloseable, SwiftSequence, Cnext/freefunctions.
The cursor wraps the core Cursor behind a Mutex, because next_batch takes &mut self. After close(), next_batch returns the end or a clear error, never a panic. PreparedQuery.query follows when PreparedQuery is exposed (#181, v3.1.0).
Checklist
-
minigraf-python(Tier 1, uniffi) -
minigraf-java(uniffi) -
minigraf-android(uniffi) -
minigraf-swift(uniffi) -
minigraf-node(napi) -
minigraf-wasm(wasm-bindgen / WASI) -
minigraf-c(cbindgen:minigraf_query,minigraf_cursor_next_batch,minigraf_cursor_free) -
BrowserDbin core (src/browser/,browserfeature) -
minigraf-binding-templateupdated so new bindings start with it
Acceptance
- In each binding, batches of 1, 7 and 1,000 return the same rows as
execute(),close()after the first batch works, and a non-query givesAPI-012. - A cursor opened before a
transactreturns the answer from before it. - The Python round trip with batches of 1,000 is recorded in the perf: benchmark queries at 100K–10M facts and publish a performance envelope #394 performance envelope, as enhancement (v3.0.0): rework the query and transaction engine as a streaming, pull-based operator pipeline with bounded memory #432's acceptance list asks.
Part of #432.
Done (2026-10-07)
All on each repo's v3 branch, pinned to core v3 by git revision:
minigraf-python#6, minigraf-java#9, minigraf-android#6, minigraf-swift#6, minigraf-node#5, minigraf-c#4, minigraf-wasm#4, minigraf-binding-template#1. BrowserDb.query → BrowserCursor in core #468. Every binding tests batches of 1/7/1000 against execute(), close() after the first batch, a cursor fixed at open, and API-012. The WASI package ships only the REPL binary, so it has no cursor to expose. The shared shape is in docs/superpowers/specs/2026-10-07-binding-etl-surface-design.md.
The last acceptance item (Python batch-1,000 round trip in the #394 envelope) moves to #394. #432's acceptance list has it too.
Activity
Merge pull request #468 from project-minigraf/feat/binding-etl-surface
feat: open options, query cursors, fact log and log writer (v3) (#6)
feat: open options, query cursors, fact log and log writer (v3) (#5)
feat: open options, query cursors, fact log and log writer (v3) (#4)
feat: BrowserDb query cursor and v3 core (v3) (#4)
feat: shared v3 shim — open options, cursors, fact log, log writer (#1)
adityamukho commentedon Oct 7, 2026
All checklist items done; see the 'Done' section in the description. Ships in v3.0.0.