superbigcup325/moonlake/engine does not have a README file

    BoundQuery

    pub struct BoundQuery {
    // private fields
    } derive(
    Debug
    )

    The bound query, ready for the executor.

    ExecOptions

    pub(all) struct ExecOptions {
    budget : Int
    spill : SpillStore?
    }

    ExecOptions::in_memory

    fn ExecOptions::in_memory() -> ExecOptions

    Default options: unlimited memory, no spill store — the historical in-memory behavior.

    JsonRow

    pub struct JsonRow {
    // private fields
    }

    A result row as a JSON object, in the layout to_json prints. The caller supplies the surrounding [, the commas between batches and the ], so a result can be written as its batches arrive instead of being collected first. Keys are made unique exactly as to_json makes them.

    JsonRow::batch

    The rows of batch as comma-separated JSON objects, without brackets: a writer that prints [, each batch of the stream in turn, and ] prints exactly what to_json prints for the whole result.

    JsonRow::new

    fn JsonRow::new(names : Array[String]) -> JsonRow

    JsonRow::render

    fn JsonRow::render(self : JsonRow, batch :
    DataBatch
    , row : Int) -> String

    One row as JSON: {"a":1,"b":null}.

    Residency

    pub(all) struct Residency {
    read : () -> (Int, Int)
    }

    The residency reader a profiling run may inject: it returns the process's resident KB right now and its high-water KB. The native CLI supplies one that reads /proc; a target without it supplies none, and the report then prints no resident fields at all rather than a zero that would pass for a small residency (contract: docs/ROADMAP.md).

    ResultStream

    pub struct ResultStream {
    // private fields
    }

    A result the caller consumes batch by batch.

    What the batches promise is the batch contract above: whole batches only, None exactly once at the end, no zero-row batch, and a batch belongs to the caller. What the shape promises:

    • A single-table scan is pulled: a filter, the projection and an OFFSET/LIMIT window run per batch, so the caller never holds more than one batch and a windowed query stops reading when the window is full.
    • An aggregate fold reads the scan rather than a collected input: the group table is a running hash table, so the answer is one batch but the input rows never are. A budget does not change that: the batches are buffered up to the budget and partitioned into spill runs only past it.
    • A spilled ORDER BY is pulled from its run files: the merge hands rows back in order, and a windowed query stops merging once its window is full.
    • A two-table INNER/LEFT/FULL join is pulled: the build side is read once and stays resident — a probe row has to find its matches — but the probe side arrives a batch at a time and the joined rows leave in batches, so neither the probe side nor the output is ever held whole.
    • Every other shape (a chain of two or more joins, a RIGHT or SEMI/ANTI join, a keyless cross join, or anything above a join that needs every row first: ORDER BY, GROUP BY, DISTINCT, a window function, a derived table, a set operation) is executed eagerly here, and next yields that result as one batch. The rows are the ones execute returns for the same query.

    The lifecycle is short enough to state whole:

    • Exhaustion: next returns None at the end and keeps returning None; there is no second end and no reset.
    • Early stop: close stops the pull, is idempotent, is safe before the first next, and leaves next returning None.
    • Failure: a pull that raises ends the stream, so a retry after an error reports the end rather than resuming a scan whose state is unknown.
    • Cleanup: dropping the stream releases the scan cursor. A spilled sort goes further: a sort under a budget writes run files and hands out their merge, so it owns files this stream removes when the merge ends, when close stops it, and when it raises. A join owns nothing of its own — its build side is resident and its probe side belongs to the catalog — so close only stops probing. A consumer that vanishes without either leaves them to the store's owner — the CLI deletes its per-query directory at exit, and a library caller's SpillStore is the caller's to sweep. Exhaust the stream or close it and nothing is left behind. catalog.BatchStream gains no close method for this: exhaustion is its whole protocol, and adding one would break every implementation outside this module.

    ResultStream::close

    fn ResultStream::close(self : ResultStream) -> Unit

    Stop the pull. Idempotent, and early: a caller that has what it needs says so instead of draining the scan. A stream over spilled runs deletes them here, so a caller that stops early releases its disk too.

    ResultStream::names

    fn ResultStream::names(self : ResultStream) -> Array[String]

    ResultStream::next

    The next batch, or None once the result is exhausted or close was called. A pull that raises ends the stream: the stream is finished either way, and a caller that retries after an error gets the end, not a resumed scan.

    SelectResult

    pub struct SelectResult {
    // private fields
    }

    SelectResult::get

    Scalar cell accessor for result consumption (row, column).

    SelectResult::names

    fn SelectResult::names(self : SelectResult) -> Array[String]

    SelectResult::row_count

    fn SelectResult::row_count(self : SelectResult) -> Int

    SelectResult::to_json

    fn SelectResult::to_json(self : SelectResult) -> String

    Whole result as one JSON array line. Strings are escaped; SQL NULL becomes null. DATE renders as a quoted calendar date (DuckDB's own JSON spelling), not a bare epoch integer. Non-finite FLOAT64 values have no JSON number spelling, so they render as the strings "Infinity", "-Infinity" and "NaN"; that keeps the output parseable while staying distinct from both finite numbers and SQL NULL. Two output columns can share a label (SELECT a, b AS a): object keys must be unique, so repeats get a _1 suffix (a CSV header's rename) instead of a key that silently overwrites its neighbour in a JSON parser.

    SpillStore

    pub(all) struct SpillStore {
    write : (Bytes) -> Int raise
    SqlError

    read : (Int) -> Bytes raise
    SqlError

    remove : (Int) -> Unit raise
    SqlError

    }

    Storage for spilled intermediate state (sorted runs today). Ids are opaque and scoped to one query; the engine removes everything it wrote when the query ends, successfully or not.

    explain

    Render the physical plan without executing it: join steps with pushdown classification, aggregation, projection, ordering.

    explain_analyze

    Run the query and render the physical plan annotated with the actual row counts each stage produced — scans, join outputs, aggregate groups and the final result — plus what it spilled, if anything. No clocks by default: row counts and byte counts are the deterministic part of ANALYZE, and they stay the whole report unless the caller installs one with set_stage_clock. A caller that does (the CLI's --analyze on a target with a monotonic clock) also gets a time <stage> (ms) line per stage, including each join step.

    explain_analyze_with

    fn explain_analyze_with(sql : String, catalog :
    Catalog
    , opts : ExecOptions) -> String raise
    SqlError

    explain_analyze under explicit execution options. ANALYZE runs the query, so the budget has to come in here as well: a plan rendered under ExecOptions::in_memory() can never show a spill, which is exactly the case a reader of ANALYZE wants to see.

    mem_report_with

    fn mem_report_with(sql : String, catalog :
    Catalog
    , opts : ExecOptions, sample? : Residency?) -> String raise
    SqlError

    Run the query with the structure census on and print what each declared point held, largest first: the checkpoints and what they count are the contract in docs/ROADMAP.md. The answer is dropped, because this is a measurement rather than a result API; the text is empty when the query reached no declared point. A census run takes slightly longer than the plain run (it sizes every structure it names). A caller that can read the process's residency (native /proc) passes a sampler and the report names where the peak was, instead of implying that its largest named point is that peak (§62).

    run_select_opts

    Execute with explicit options (memory budget + spill store).

    run_select_stream

    Execute a parsed select and hand the rows out as they come. See ResultStream for what a caller may assume.

    run_select_stream_opts

    run_select_stream under explicit options. Three shapes are pulled rather than materialized: a single table narrowed by a filter, projection and window; a two-table join whose build side is read once and whose probe side arrives a batch at a time; and a spilled ORDER BY, whose runs this stream owns. A global aggregate folds the scan as it arrives but still hands back its one-row answer as one batch. Everything else is computed first and handed back as one batch.

    set_stage_clock

    fn set_stage_clock(clock : () -> Int64?) -> Unit

    Install the clock stage timings read (nanoseconds, monotonic), or None to leave them out of the report. The CLI sets it for --analyze alone; a library caller that does not set it sees the same deterministic report it always did.