77 lines
3.4 KiB
Markdown
77 lines
3.4 KiB
Markdown
## Plan Exporter
|
|
|
|
Haskell tool that turns a hand-authored scenario into the JSON IR consumed by
|
|
[`crates/plan-runner`](../../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`](../../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
|
|
|
|
```text
|
|
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.
|
|
> A system GHC older than 9.12 will fail to compile geolog-lang's `GHC2024` modules.
|
|
|
|
```sh
|
|
# 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`](../../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.
|