2026-06-05 13:40:16 +02:00
..
2026-06-05 13:40:16 +02:00
2026-06-05 13:40:16 +02:00
2026-06-05 13:40:16 +02:00
2026-06-05 13:40:16 +02:00
2026-06-05 13:40:16 +02:00

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 (or nix develop) before running the commands below.

# 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.