Plan Exporter
Haskell tool that turns a hand-authored scenario into the JSON IR consumed by
crates/plan-runner.
Each scenario carries a small schema, a set of ground facts, a list of conjunctive-query atoms, and an expected-bindings oracle.
The exporter parses the scenario, runs Geolog.DB.Plan.planConjunction from
external/geolog to produce a Yannakakis-style plan,
self-checks the planned bindings against the oracle, then writes the runner IR to stdout.
This is the only producer of runner IR today; any other frontend that emits the same JSON shape can drive the runner instead.
Layout
tools/exporter/
├── plan-exporter.cabal # cabal package: executable `plan-export`
├── cabal.project # points at geolog-lang and its siblings in external/geolog
├── src/Main.hs # the exporter itself; rustdoc-style header explains the IR
└── examples/ # hand-authored scenario inputs
├── cartesian.scenario.json
├── self_loop.scenario.json
├── three_atom_chain.scenario.json
└── two_atom_join.scenario.json
Run It
⚠️ The exporter needs GHC 9.12 and Cabal. The repository's Nix dev shell provides both; enter it with
make shell(ornix develop) before running the commands below. A system GHC older than 9.12 will fail to compile geolog-lang'sGHC2024modules.
# Build the executable:
cd tools/exporter && cabal build plan-export
# Export one scenario to runner IR JSON:
cabal run -v0 plan-export -- examples/two_atom_join.scenario.json
# Regenerate every fixture in crates/plan-runner/fixtures/ from these scenarios:
make export-fixtures # run from the repository root
Scenario Format
Each .scenario.json carries four blocks:
| Block | Maps to |
|---|---|
schema |
FlatTheory from geolog-lang: relation paths and column types. |
facts |
Ground tuples per relation, in the shape InMemory ingests. |
atoms |
The conjunctive-query body, as a list of QAtom. |
expected_bindings |
Variables and rows the planner is expected to produce. Used as a self-check; lifted through to the runner-side oracle. |
See the four committed examples for the exact JSON shape.
The runner-side IR shape (what the exporter emits) is documented in
crates/plan-runner/src/lib.rs.
Self-Check
Before emitting JSON, the exporter runs the planned query through
Geolog.DB.InMemory.evalConjunctionPlanned and asserts the bindings match
the scenario's expected_bindings.
A mismatched scenario fails at export time, so the Rust side never sees a broken fixture.
Notes
- Cross-language contract.
The JSON shape is the contract between this tool and
plan-runner. Changing it means editing both sides. - Subset of the source language.
The exporter only handles conjunctive queries that fit
planConjunction. Negation, recursion, and laws are out of scope here; they belong to a different planner pass. - No durable references to ignored paths.
This tool depends on
external/geolog(a submodule), not on any ad-hoc clone or generated copy in an ignored path.