WIP
@ -8,7 +8,7 @@ indent_size = 4
|
|||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.rs]
|
[*.{rs,hs,py}]
|
||||||
max_line_length = 100
|
max_line_length = 100
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
|
|||||||
@ -50,7 +50,7 @@ Expected durable areas may include:
|
|||||||
|
|
||||||
- `src/`: Rust source for parser, catalog, planner, execution experiments, and storage prototypes.
|
- `src/`: Rust source for parser, catalog, planner, execution experiments, and storage prototypes.
|
||||||
- `tests/`: integration tests for rule planning, evaluation, and storage behavior.
|
- `tests/`: integration tests for rule planning, evaluation, and storage behavior.
|
||||||
- `examples/`: small runnable Datalog-like programs or storage scenarios.
|
- `tools/exporter/examples/`: hand-authored scenario JSON consumed by the Haskell exporter to produce runner fixtures.
|
||||||
- `fixtures/`: committed input facts and expected outputs.
|
- `fixtures/`: committed input facts and expected outputs.
|
||||||
- `notes/`: local design notes that belong to this project.
|
- `notes/`: local design notes that belong to this project.
|
||||||
- `flowlog/`: project-local notes or sketches derived from the FlowLog line of work.
|
- `flowlog/`: project-local notes or sketches derived from the FlowLog line of work.
|
||||||
|
|||||||
21
Cargo.lock
generated
@ -555,16 +555,6 @@ dependencies = [
|
|||||||
"wasip3",
|
"wasip3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "glog-runner"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"query-ops",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"storage",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "guardian"
|
name = "guardian"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -1156,6 +1146,17 @@ version = "0.3.33"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plan-runner"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"query-ops",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"storage",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotters"
|
name = "plotters"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
|||||||
20
Makefile
@ -77,22 +77,28 @@ clean: ## Remove build output
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
EXPORTER_DIR := tools/exporter
|
EXPORTER_DIR := tools/exporter
|
||||||
EXPORTER_FIXTURES := crates/glog-runner/fixtures
|
EXPORTER_FIXTURES := crates/plan-runner/fixtures
|
||||||
EXPORTER_SCENARIOS := three-atom-chain
|
EXAMPLES_DIR := $(EXPORTER_DIR)/examples
|
||||||
|
|
||||||
.PHONY: export-fixtures
|
.PHONY: export-fixtures
|
||||||
export-fixtures: ## Regenerate JSON plan fixtures from the Haskell exporter (needs Cabal and GHC; use `make shell` first).
|
export-fixtures: ## Regenerate plan JSON for every tools/exporter/examples/*.scenario.json (needs Cabal and GHC; use `make shell` first).
|
||||||
@if ! command -v cabal >/dev/null 2>&1; then \
|
@if ! command -v cabal >/dev/null 2>&1; then \
|
||||||
echo "cabal not found. Enter the dev shell with 'make shell' (or 'nix develop') first."; \
|
echo "cabal not found. Enter the dev shell with 'make shell' (or 'nix develop') first."; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
@cd $(EXPORTER_DIR) && cabal build glog-export
|
@cd $(EXPORTER_DIR) && cabal build plan-export
|
||||||
@for sc in $(EXPORTER_SCENARIOS); do \
|
@mkdir -p $(EXPORTER_FIXTURES)
|
||||||
out=$(EXPORTER_FIXTURES)/$$(echo $$sc | tr '-' '_').json; \
|
@for sc in $(EXAMPLES_DIR)/*.scenario.json; do \
|
||||||
|
base=$$(basename $$sc .scenario.json); \
|
||||||
|
out=$(EXPORTER_FIXTURES)/$$base.json; \
|
||||||
echo "exporting $$sc -> $$out"; \
|
echo "exporting $$sc -> $$out"; \
|
||||||
(cd $(EXPORTER_DIR) && cabal run -v0 glog-export -- $$sc) > $$out; \
|
(cd $(EXPORTER_DIR) && cabal run -v0 plan-export -- examples/$$base.scenario.json) > $$out; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
.PHONY: examples
|
||||||
|
examples: export-fixtures ## Regenerate fixtures from scenarios and run them through plan-runner against their oracles.
|
||||||
|
@cargo test -p plan-runner --test examples
|
||||||
|
|
||||||
.PHONY: shell
|
.PHONY: shell
|
||||||
shell: ## Enter the Nix dev shell defined in flake.nix
|
shell: ## Enter the Nix dev shell defined in flake.nix
|
||||||
@nix develop
|
@nix develop
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
## Storage Engine Playground
|
## Storage Engine Playground
|
||||||
|
|
||||||
This repo is a playground for running small experiments related to things like FlowLog, DBSP, Geomerge, etc.
|
This repo is a playground for running small experiments related to storage side of things.
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
|
|||||||
@ -5,381 +5,239 @@
|
|||||||
-->
|
-->
|
||||||
<!-- Title: GeomergeDemoWorkflow Pages: 1 -->
|
<!-- Title: GeomergeDemoWorkflow Pages: 1 -->
|
||||||
<svg width="3020pt" height="407pt"
|
<svg width="3020pt" height="407pt"
|
||||||
viewBox="0.00 0.00 3020.25 407.00" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0.00 0.00 3020.25 407.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 403)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 403)">
|
||||||
<title>GeomergeDemoWorkflow</title>
|
<title>GeomergeDemoWorkflow</title>
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-403 3016.25,-403 3016.25,4 -4,4"/>
|
<polygon fill="white" stroke="none" points="-4,4 -4,-403 3016.25,-403 3016.25,4 -4,4"/>
|
||||||
<g id="clust1" class="cluster">
|
<g id="clust1" class="cluster">
|
||||||
<title>cluster_inputs</title>
|
<title>cluster_inputs</title>
|
||||||
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2" points="8,-11 8,-226 202.5,-226 202.5,-11 8,-11"/>
|
||||||
points="8,-11 8,-226 202.5,-226 202.5,-11 8,-11"/>
|
<text text-anchor="middle" x="105.25" y="-208.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#555555">Inputs</text>
|
||||||
<text text-anchor="middle" x="105.25" y="-208.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#555555">Inputs
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust2" class="cluster">
|
<g id="clust2" class="cluster">
|
||||||
<title>cluster_demo</title>
|
<title>cluster_demo</title>
|
||||||
<path fill="#fafafa" stroke="#666666"
|
<path fill="#fafafa" stroke="#666666" d="M284.75,-8C284.75,-8 2574.75,-8 2574.75,-8 2580.75,-8 2586.75,-14 2586.75,-20 2586.75,-20 2586.75,-304 2586.75,-304 2586.75,-310 2580.75,-316 2574.75,-316 2574.75,-316 284.75,-316 284.75,-316 278.75,-316 272.75,-310 272.75,-304 272.75,-304 272.75,-20 272.75,-20 272.75,-14 278.75,-8 284.75,-8"/>
|
||||||
d="M284.75,-8C284.75,-8 2574.75,-8 2574.75,-8 2580.75,-8 2586.75,-14 2586.75,-20 2586.75,-20 2586.75,-304 2586.75,-304 2586.75,-310 2580.75,-316 2574.75,-316 2574.75,-316 284.75,-316 284.75,-316 278.75,-316 272.75,-310 272.75,-304 272.75,-304 272.75,-20 272.75,-20 272.75,-14 278.75,-8 284.75,-8"/>
|
<text text-anchor="middle" x="1429.75" y="-298.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#333333">geomerge-demo (run_demo)</text>
|
||||||
<text text-anchor="middle" x="1429.75" y="-298.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#333333">geomerge-demo (run_demo)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust3" class="cluster">
|
<g id="clust3" class="cluster">
|
||||||
<title>cluster_loading</title>
|
<title>cluster_loading</title>
|
||||||
<polygon fill="#fafafa" stroke="#9c27b0" stroke-dasharray="5,2"
|
<polygon fill="#fafafa" stroke="#9c27b0" stroke-dasharray="5,2" points="292.75,-116 292.75,-267 609.25,-267 609.25,-116 292.75,-116"/>
|
||||||
points="292.75,-116 292.75,-267 609.25,-267 609.25,-116 292.75,-116"/>
|
<text text-anchor="middle" x="451" y="-249.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#7b1fa2">Theory Loading</text>
|
||||||
<text text-anchor="middle" x="451" y="-249.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#7b1fa2">Theory Loading
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust4" class="cluster">
|
<g id="clust4" class="cluster">
|
||||||
<title>cluster_store</title>
|
<title>cluster_store</title>
|
||||||
<polygon fill="#fafafa" stroke="#4caf50" stroke-dasharray="5,2"
|
<polygon fill="#fafafa" stroke="#4caf50" stroke-dasharray="5,2" points="648.25,-28 648.25,-267 1744,-267 1744,-28 648.25,-28"/>
|
||||||
points="648.25,-28 648.25,-267 1744,-267 1744,-28 648.25,-28"/>
|
<text text-anchor="middle" x="1196.12" y="-249.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#388e3c">Storage and Transaction</text>
|
||||||
<text text-anchor="middle" x="1196.12" y="-249.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#388e3c">Storage and Transaction
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust5" class="cluster">
|
<g id="clust5" class="cluster">
|
||||||
<title>cluster_persist</title>
|
<title>cluster_persist</title>
|
||||||
<polygon fill="#fafafa" stroke="#ff9800" stroke-dasharray="5,2"
|
<polygon fill="#fafafa" stroke="#ff9800" stroke-dasharray="5,2" points="1809,-77 1809,-267 2566.75,-267 2566.75,-77 1809,-77"/>
|
||||||
points="1809,-77 1809,-267 2566.75,-267 2566.75,-77 1809,-77"/>
|
<text text-anchor="middle" x="2187.88" y="-249.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#f57c00">Persistence Round Trip</text>
|
||||||
<text text-anchor="middle" x="2187.88" y="-249.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#f57c00">Persistence Round Trip
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust6" class="cluster">
|
<g id="clust6" class="cluster">
|
||||||
<title>cluster_report</title>
|
<title>cluster_report</title>
|
||||||
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2" points="2610.75,-174 2610.75,-391 3004.25,-391 3004.25,-174 2610.75,-174"/>
|
||||||
points="2610.75,-174 2610.75,-391 3004.25,-391 3004.25,-174 2610.75,-174"/>
|
<text text-anchor="middle" x="2807.5" y="-373.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#555555">Report</text>
|
||||||
<text text-anchor="middle" x="2807.5" y="-373.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#555555">Report
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- paths_schema -->
|
<!-- paths_schema -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
<title>paths_schema</title>
|
<title>paths_schema</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M159,-179C159,-179 51.5,-179 51.5,-179 45.5,-179 39.5,-173 39.5,-167 39.5,-167 39.5,-141 39.5,-141 39.5,-135 45.5,-129 51.5,-129 51.5,-129 159,-129 159,-129 165,-129 171,-135 171,-141 171,-141 171,-167 171,-167 171,-173 165,-179 159,-179"/>
|
||||||
d="M159,-179C159,-179 51.5,-179 51.5,-179 45.5,-179 39.5,-173 39.5,-167 39.5,-167 39.5,-141 39.5,-141 39.5,-135 45.5,-129 51.5,-129 51.5,-129 159,-129 159,-129 165,-129 171,-135 171,-141 171,-141 171,-167 171,-167 171,-173 165,-179 159,-179"/>
|
<text text-anchor="middle" x="105.25" y="-161.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">paths.json</text>
|
||||||
<text text-anchor="middle" x="105.25" y="-161.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="105.25" y="-140.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(compiled schema)</text>
|
||||||
paths.json
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="105.25" y="-140.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
(compiled schema)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- load_theory -->
|
<!-- load_theory -->
|
||||||
<g id="node3" class="node">
|
<g id="node3" class="node">
|
||||||
<title>load_theory</title>
|
<title>load_theory</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M422.5,-191C422.5,-191 318.75,-191 318.75,-191 312.75,-191 306.75,-185 306.75,-179 306.75,-179 306.75,-153 306.75,-153 306.75,-147 312.75,-141 318.75,-141 318.75,-141 422.5,-141 422.5,-141 428.5,-141 434.5,-147 434.5,-153 434.5,-153 434.5,-179 434.5,-179 434.5,-185 428.5,-191 422.5,-191"/>
|
||||||
d="M422.5,-191C422.5,-191 318.75,-191 318.75,-191 312.75,-191 306.75,-185 306.75,-179 306.75,-179 306.75,-153 306.75,-153 306.75,-147 312.75,-141 318.75,-141 318.75,-141 422.5,-141 422.5,-141 428.5,-141 434.5,-147 434.5,-153 434.5,-153 434.5,-179 434.5,-179 434.5,-185 428.5,-191 422.5,-191"/>
|
<text text-anchor="middle" x="370.62" y="-173.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">load_paths_theory</text>
|
||||||
<text text-anchor="middle" x="370.62" y="-173.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="370.62" y="-152.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(serde_json)</text>
|
||||||
load_paths_theory
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="370.62" y="-152.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
(serde_json)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- paths_schema->load_theory -->
|
<!-- paths_schema->load_theory -->
|
||||||
<g id="edge1" class="edge">
|
<g id="edge1" class="edge">
|
||||||
<title>paths_schema->load_theory</title>
|
<title>paths_schema->load_theory</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2"
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M171.35,-156.97C208.77,-158.67 255.91,-160.82 294.83,-162.59"/>
|
||||||
d="M171.35,-156.97C208.77,-158.67 255.91,-160.82 294.83,-162.59"/>
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="294.67,-166.09 304.82,-163.05 294.99,-159.1 294.67,-166.09"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
<text text-anchor="middle" x="241.12" y="-166.02" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">include_str!</text>
|
||||||
points="294.67,-166.09 304.82,-163.05 294.99,-159.1 294.67,-166.09"/>
|
|
||||||
<text text-anchor="middle" x="241.12" y="-166.02" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">include_str!
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- fixture_rows -->
|
<!-- fixture_rows -->
|
||||||
<g id="node2" class="node">
|
<g id="node2" class="node">
|
||||||
<title>fixture_rows</title>
|
<title>fixture_rows</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M172.5,-79C172.5,-79 38,-79 38,-79 32,-79 26,-73 26,-67 26,-67 26,-41 26,-41 26,-35 32,-29 38,-29 38,-29 172.5,-29 172.5,-29 178.5,-29 184.5,-35 184.5,-41 184.5,-41 184.5,-67 184.5,-67 184.5,-73 178.5,-79 172.5,-79"/>
|
||||||
d="M172.5,-79C172.5,-79 38,-79 38,-79 32,-79 26,-73 26,-67 26,-67 26,-41 26,-41 26,-35 32,-29 38,-29 38,-29 172.5,-29 172.5,-29 178.5,-29 184.5,-35 184.5,-41 184.5,-41 184.5,-67 184.5,-67 184.5,-73 178.5,-79 172.5,-79"/>
|
<text text-anchor="middle" x="105.25" y="-61.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Fixture Rows</text>
|
||||||
<text text-anchor="middle" x="105.25" y="-61.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="105.25" y="-40.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(graphs, vertices, edge)</text>
|
||||||
Fixture Rows
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="105.25" y="-40.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
(graphs, vertices, edge)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- transact -->
|
<!-- transact -->
|
||||||
<g id="node6" class="node">
|
<g id="node6" class="node">
|
||||||
<title>transact</title>
|
<title>transact</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1147.25,-223.62C1147.25,-223.62 950.75,-223.62 950.75,-223.62 944.75,-223.62 938.75,-217.62 938.75,-211.62 938.75,-211.62 938.75,-54.38 938.75,-54.38 938.75,-48.38 944.75,-42.38 950.75,-42.38 950.75,-42.38 1147.25,-42.38 1147.25,-42.38 1153.25,-42.38 1159.25,-48.38 1159.25,-54.38 1159.25,-54.38 1159.25,-211.62 1159.25,-211.62 1159.25,-217.62 1153.25,-223.62 1147.25,-223.62"/>
|
||||||
d="M1147.25,-223.62C1147.25,-223.62 950.75,-223.62 950.75,-223.62 944.75,-223.62 938.75,-217.62 938.75,-211.62 938.75,-211.62 938.75,-54.38 938.75,-54.38 938.75,-48.38 944.75,-42.38 950.75,-42.38 950.75,-42.38 1147.25,-42.38 1147.25,-42.38 1153.25,-42.38 1159.25,-48.38 1159.25,-54.38 1159.25,-54.38 1159.25,-211.62 1159.25,-211.62 1159.25,-217.62 1153.25,-223.62 1147.25,-223.62"/>
|
<text text-anchor="start" x="954.88" y="-203.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">add_paths_data (tx.insert ×7)</text>
|
||||||
<text text-anchor="start" x="954.88" y="-203.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="950.75" y="-174.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• insert Graphs rows</text>
|
||||||
font-size="14.00">add_paths_data (tx.insert ×7)
|
<text text-anchor="start" x="950.75" y="-145.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• insert G0, G1 rows</text>
|
||||||
</text>
|
<text text-anchor="start" x="950.75" y="-116.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• insert G.V vertices</text>
|
||||||
<text text-anchor="start" x="950.75" y="-174.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="950.75" y="-87.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• insert G.E edge</text>
|
||||||
• insert Graphs rows
|
<text text-anchor="start" x="950.75" y="-58.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• pending RowIds reused as FKs</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="950.75" y="-145.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• insert G0, G1 rows
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="950.75" y="-116.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• insert G.V vertices
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="950.75" y="-87.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
insert G.E edge
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="950.75" y="-58.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
pending RowIds reused as FKs
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- fixture_rows->transact -->
|
<!-- fixture_rows->transact -->
|
||||||
<g id="edge5" class="edge">
|
<g id="edge5" class="edge">
|
||||||
<title>fixture_rows->transact</title>
|
<title>fixture_rows->transact</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" stroke-dasharray="5,2" d="M184.91,-54C237.25,-54 307.55,-54 369.62,-54 369.62,-54 369.62,-54 768,-54 821.42,-54 878.49,-67.48 927.08,-83.31"/>
|
||||||
d="M184.91,-54C237.25,-54 307.55,-54 369.62,-54 369.62,-54 369.62,-54 768,-54 821.42,-54 878.49,-67.48 927.08,-83.31"/>
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="925.95,-86.62 936.55,-86.47 928.17,-79.98 925.95,-86.62"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
|
||||||
points="925.95,-86.62 936.55,-86.47 928.17,-79.98 925.95,-86.62"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- flat_theory -->
|
<!-- flat_theory -->
|
||||||
<g id="node4" class="node">
|
<g id="node4" class="node">
|
||||||
<title>flat_theory</title>
|
<title>flat_theory</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M583.25,-224.12C583.25,-224.12 513.5,-224.12 513.5,-224.12 507.5,-224.12 501.5,-218.12 501.5,-212.12 501.5,-212.12 501.5,-141.88 501.5,-141.88 501.5,-135.88 507.5,-129.88 513.5,-129.88 513.5,-129.88 583.25,-129.88 583.25,-129.88 589.25,-129.88 595.25,-135.88 595.25,-141.88 595.25,-141.88 595.25,-212.12 595.25,-212.12 595.25,-218.12 589.25,-224.12 583.25,-224.12"/>
|
||||||
d="M583.25,-224.12C583.25,-224.12 513.5,-224.12 513.5,-224.12 507.5,-224.12 501.5,-218.12 501.5,-212.12 501.5,-212.12 501.5,-141.88 501.5,-141.88 501.5,-135.88 507.5,-129.88 513.5,-129.88 513.5,-129.88 583.25,-129.88 583.25,-129.88 589.25,-129.88 595.25,-135.88 595.25,-141.88 595.25,-141.88 595.25,-212.12 595.25,-212.12 595.25,-218.12 589.25,-224.12 583.25,-224.12"/>
|
<text text-anchor="start" x="513.5" y="-203.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">FlatTheory</text>
|
||||||
<text text-anchor="start" x="513.5" y="-203.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="513.5" y="-174.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• 10 tables</text>
|
||||||
font-size="14.00">FlatTheory
|
<text text-anchor="start" x="513.5" y="-145.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• 12 laws</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="513.5" y="-174.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
10 tables
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="513.5" y="-145.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
12 laws
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- load_theory->flat_theory -->
|
<!-- load_theory->flat_theory -->
|
||||||
<g id="edge2" class="edge">
|
<g id="edge2" class="edge">
|
||||||
<title>load_theory->flat_theory</title>
|
<title>load_theory->flat_theory</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M434.58,-169.94C452.51,-171.06 471.95,-172.28 489.56,-173.38"/>
|
||||||
d="M434.58,-169.94C452.51,-171.06 471.95,-172.28 489.56,-173.38"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="489.11,-176.86 499.31,-173.99 489.55,-169.87 489.11,-176.86"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
|
||||||
points="489.11,-176.86 499.31,-173.99 489.55,-169.87 489.11,-176.86"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- build_store -->
|
<!-- build_store -->
|
||||||
<g id="node5" class="node">
|
<g id="node5" class="node">
|
||||||
<title>build_store</title>
|
<title>build_store</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M859.75,-186C859.75,-186 674.25,-186 674.25,-186 668.25,-186 662.25,-180 662.25,-174 662.25,-174 662.25,-148 662.25,-148 662.25,-142 668.25,-136 674.25,-136 674.25,-136 859.75,-136 859.75,-136 865.75,-136 871.75,-142 871.75,-148 871.75,-148 871.75,-174 871.75,-174 871.75,-180 865.75,-186 859.75,-186"/>
|
||||||
d="M859.75,-186C859.75,-186 674.25,-186 674.25,-186 668.25,-186 662.25,-180 662.25,-174 662.25,-174 662.25,-148 662.25,-148 662.25,-142 668.25,-136 674.25,-136 674.25,-136 859.75,-136 859.75,-136 865.75,-136 871.75,-142 871.75,-148 871.75,-148 871.75,-174 871.75,-174 871.75,-180 865.75,-186 859.75,-186"/>
|
<text text-anchor="middle" x="767" y="-168.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">GeomergeStorage::from_theory</text>
|
||||||
<text text-anchor="middle" x="767" y="-168.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="767" y="-147.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(Store::try_from_theory)</text>
|
||||||
GeomergeStorage::from_theory
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="767" y="-147.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
(Store::try_from_theory)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- flat_theory->build_store -->
|
<!-- flat_theory->build_store -->
|
||||||
<g id="edge3" class="edge">
|
<g id="edge3" class="edge">
|
||||||
<title>flat_theory->build_store</title>
|
<title>flat_theory->build_store</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M595.58,-173.59C611.81,-172.39 630.85,-170.98 650.22,-169.55"/>
|
||||||
d="M595.58,-173.59C611.81,-172.39 630.85,-170.98 650.22,-169.55"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="650.44,-173.05 660.15,-168.82 649.92,-166.06 650.44,-173.05"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
|
||||||
points="650.44,-173.05 660.15,-168.82 649.92,-166.06 650.44,-173.05"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- demo_report -->
|
<!-- demo_report -->
|
||||||
<g id="node13" class="node">
|
<g id="node13" class="node">
|
||||||
<title>demo_report</title>
|
<title>demo_report</title>
|
||||||
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5"
|
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5" d="M2810.25,-344.12C2810.25,-344.12 2640.75,-344.12 2640.75,-344.12 2634.75,-344.12 2628.75,-338.12 2628.75,-332.12 2628.75,-332.12 2628.75,-203.88 2628.75,-203.88 2628.75,-197.88 2634.75,-191.88 2640.75,-191.88 2640.75,-191.88 2810.25,-191.88 2810.25,-191.88 2816.25,-191.88 2822.25,-197.88 2822.25,-203.88 2822.25,-203.88 2822.25,-332.12 2822.25,-332.12 2822.25,-338.12 2816.25,-344.12 2810.25,-344.12"/>
|
||||||
d="M2810.25,-344.12C2810.25,-344.12 2640.75,-344.12 2640.75,-344.12 2634.75,-344.12 2628.75,-338.12 2628.75,-332.12 2628.75,-332.12 2628.75,-203.88 2628.75,-203.88 2628.75,-197.88 2634.75,-191.88 2640.75,-191.88 2640.75,-191.88 2810.25,-191.88 2810.25,-191.88 2816.25,-191.88 2822.25,-197.88 2822.25,-203.88 2822.25,-203.88 2822.25,-332.12 2822.25,-332.12 2822.25,-338.12 2816.25,-344.12 2810.25,-344.12"/>
|
<text text-anchor="start" x="2684.25" y="-323.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">DemoReport</text>
|
||||||
<text text-anchor="start" x="2684.25" y="-323.82" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="start" x="2640.75" y="-294.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• table_count, law_count</text>
|
||||||
font-weight="bold" font-size="14.00">DemoReport
|
<text text-anchor="start" x="2640.75" y="-265.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• graph, vertex, edge counts</text>
|
||||||
</text>
|
<text text-anchor="start" x="2640.75" y="-236.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• edge endpoints</text>
|
||||||
<text text-anchor="start" x="2640.75" y="-294.57" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="start" x="2640.75" y="-207.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• persisted_bytes</text>
|
||||||
font-size="14.00">• table_count, law_count
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="2640.75" y="-265.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• graph, vertex, edge counts
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="2640.75" y="-236.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• edge endpoints
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="2640.75" y="-207.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• persisted_bytes
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- flat_theory->demo_report -->
|
<!-- flat_theory->demo_report -->
|
||||||
<g id="edge14" class="edge">
|
<g id="edge14" class="edge">
|
||||||
<title>flat_theory->demo_report</title>
|
<title>flat_theory->demo_report</title>
|
||||||
<path fill="none" stroke="#607d8b" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#607d8b" stroke-width="1.2" stroke-dasharray="5,2" d="M586.43,-224.45C603.41,-243.31 624.95,-263.65 648.25,-277 695.11,-303.85 711.99,-307 766,-307 766,-307 766,-307 2483.88,-307 2527.78,-307 2575.62,-300.59 2616.99,-292.98"/>
|
||||||
d="M586.43,-224.45C603.41,-243.31 624.95,-263.65 648.25,-277 695.11,-303.85 711.99,-307 766,-307 766,-307 766,-307 2483.88,-307 2527.78,-307 2575.62,-300.59 2616.99,-292.98"/>
|
<polygon fill="#607d8b" stroke="#607d8b" stroke-width="1.2" points="2617.52,-296.44 2626.7,-291.14 2616.22,-289.56 2617.52,-296.44"/>
|
||||||
<polygon fill="#607d8b" stroke="#607d8b" stroke-width="1.2"
|
<text text-anchor="middle" x="1776.5" y="-311.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">counts</text>
|
||||||
points="2617.52,-296.44 2626.7,-291.14 2616.22,-289.56 2617.52,-296.44"/>
|
|
||||||
<text text-anchor="middle" x="1776.5" y="-311.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">counts
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- build_store->transact -->
|
<!-- build_store->transact -->
|
||||||
<g id="edge4" class="edge">
|
<g id="edge4" class="edge">
|
||||||
<title>build_store->transact</title>
|
<title>build_store->transact</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M871.86,-150.61C889.82,-148.82 908.63,-146.94 927,-145.1"/>
|
||||||
d="M871.86,-150.61C889.82,-148.82 908.63,-146.94 927,-145.1"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="927.27,-148.59 936.87,-144.11 926.57,-141.63 927.27,-148.59"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="927.27,-148.59 936.87,-144.11 926.57,-141.63 927.27,-148.59"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- commit -->
|
<!-- commit -->
|
||||||
<g id="node7" class="node">
|
<g id="node7" class="node">
|
||||||
<title>commit</title>
|
<title>commit</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1483.5,-180.12C1483.5,-180.12 1238.25,-180.12 1238.25,-180.12 1232.25,-180.12 1226.25,-174.12 1226.25,-168.12 1226.25,-168.12 1226.25,-97.88 1226.25,-97.88 1226.25,-91.88 1232.25,-85.88 1238.25,-85.88 1238.25,-85.88 1483.5,-85.88 1483.5,-85.88 1489.5,-85.88 1495.5,-91.88 1495.5,-97.88 1495.5,-97.88 1495.5,-168.12 1495.5,-168.12 1495.5,-174.12 1489.5,-180.12 1483.5,-180.12"/>
|
||||||
d="M1483.5,-180.12C1483.5,-180.12 1238.25,-180.12 1238.25,-180.12 1232.25,-180.12 1226.25,-174.12 1226.25,-168.12 1226.25,-168.12 1226.25,-97.88 1226.25,-97.88 1226.25,-91.88 1232.25,-85.88 1238.25,-85.88 1238.25,-85.88 1483.5,-85.88 1483.5,-85.88 1489.5,-85.88 1495.5,-91.88 1495.5,-97.88 1495.5,-97.88 1495.5,-168.12 1495.5,-168.12 1495.5,-174.12 1489.5,-180.12 1483.5,-180.12"/>
|
<text text-anchor="start" x="1323.75" y="-159.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">tx.commit()</text>
|
||||||
<text text-anchor="start" x="1323.75" y="-159.82" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="start" x="1238.25" y="-130.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• law validation</text>
|
||||||
font-weight="bold" font-size="14.00">tx.commit()
|
<text text-anchor="start" x="1238.25" y="-101.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• CommittedTx resolves pending RowIds</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1238.25" y="-130.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• law validation
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1238.25" y="-101.58" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• CommittedTx resolves pending RowIds
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- transact->commit -->
|
<!-- transact->commit -->
|
||||||
<g id="edge6" class="edge">
|
<g id="edge6" class="edge">
|
||||||
<title>transact->commit</title>
|
<title>transact->commit</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M1159.69,-133C1177.34,-133 1195.85,-133 1214.18,-133"/>
|
||||||
d="M1159.69,-133C1177.34,-133 1195.85,-133 1214.18,-133"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1214.06,-136.5 1224.06,-133 1214.06,-129.5 1214.06,-136.5"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="1214.06,-136.5 1224.06,-133 1214.06,-129.5 1214.06,-136.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- assert_edge -->
|
<!-- assert_edge -->
|
||||||
<g id="node8" class="node">
|
<g id="node8" class="node">
|
||||||
<title>assert_edge</title>
|
<title>assert_edge</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1718,-158C1718,-158 1574.5,-158 1574.5,-158 1568.5,-158 1562.5,-152 1562.5,-146 1562.5,-146 1562.5,-120 1562.5,-120 1562.5,-114 1568.5,-108 1574.5,-108 1574.5,-108 1718,-108 1718,-108 1724,-108 1730,-114 1730,-120 1730,-120 1730,-146 1730,-146 1730,-152 1724,-158 1718,-158"/>
|
||||||
d="M1718,-158C1718,-158 1574.5,-158 1574.5,-158 1568.5,-158 1562.5,-152 1562.5,-146 1562.5,-146 1562.5,-120 1562.5,-120 1562.5,-114 1568.5,-108 1574.5,-108 1574.5,-108 1718,-108 1718,-108 1724,-108 1730,-114 1730,-120 1730,-120 1730,-146 1730,-146 1730,-152 1724,-158 1718,-158"/>
|
<text text-anchor="middle" x="1646.25" y="-140.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">assert_edge_was_stored</text>
|
||||||
<text text-anchor="middle" x="1646.25" y="-140.7" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="middle" x="1646.25" y="-119.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(storage.scan(G.E))</text>
|
||||||
font-size="14.00">assert_edge_was_stored
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="1646.25" y="-119.7" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">(storage.scan(G.E))
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- commit->assert_edge -->
|
<!-- commit->assert_edge -->
|
||||||
<g id="edge7" class="edge">
|
<g id="edge7" class="edge">
|
||||||
<title>commit->assert_edge</title>
|
<title>commit->assert_edge</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M1495.68,-133C1514.16,-133 1532.83,-133 1550.41,-133"/>
|
||||||
d="M1495.68,-133C1514.16,-133 1532.83,-133 1550.41,-133"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1550.27,-136.5 1560.27,-133 1550.27,-129.5 1550.27,-136.5"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="1550.27,-136.5 1560.27,-133 1550.27,-129.5 1550.27,-136.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- dump_before -->
|
<!-- dump_before -->
|
||||||
<g id="node9" class="node">
|
<g id="node9" class="node">
|
||||||
<title>dump_before</title>
|
<title>dump_before</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M1917.75,-159C1917.75,-159 1835,-159 1835,-159 1829,-159 1823,-153 1823,-147 1823,-147 1823,-121 1823,-121 1823,-115 1829,-109 1835,-109 1835,-109 1917.75,-109 1917.75,-109 1923.75,-109 1929.75,-115 1929.75,-121 1929.75,-121 1929.75,-147 1929.75,-147 1929.75,-153 1923.75,-159 1917.75,-159"/>
|
||||||
d="M1917.75,-159C1917.75,-159 1835,-159 1835,-159 1829,-159 1823,-153 1823,-147 1823,-147 1823,-121 1823,-121 1823,-115 1829,-109 1835,-109 1835,-109 1917.75,-109 1917.75,-109 1923.75,-109 1929.75,-115 1929.75,-121 1929.75,-121 1929.75,-147 1929.75,-147 1929.75,-153 1923.75,-159 1917.75,-159"/>
|
<text text-anchor="middle" x="1876.38" y="-141.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">store.dump()</text>
|
||||||
<text text-anchor="middle" x="1876.38" y="-141.7" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="middle" x="1876.38" y="-120.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(before persist)</text>
|
||||||
font-size="14.00">store.dump()
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="1876.38" y="-120.7" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">(before persist)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- assert_edge->dump_before -->
|
<!-- assert_edge->dump_before -->
|
||||||
<g id="edge8" class="edge">
|
<g id="edge8" class="edge">
|
||||||
<title>assert_edge->dump_before</title>
|
<title>assert_edge->dump_before</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M1730.25,-133.36C1756.87,-133.48 1785.93,-133.61 1811,-133.72"/>
|
||||||
d="M1730.25,-133.36C1756.87,-133.48 1785.93,-133.61 1811,-133.72"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="1810.76,-137.22 1820.77,-133.76 1810.79,-130.22 1810.76,-137.22"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
|
||||||
points="1810.76,-137.22 1820.77,-133.76 1810.79,-130.22 1810.76,-137.22"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- encode -->
|
<!-- encode -->
|
||||||
<g id="node10" class="node">
|
<g id="node10" class="node">
|
||||||
<title>encode</title>
|
<title>encode</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M2108,-159C2108,-159 2008.75,-159 2008.75,-159 2002.75,-159 1996.75,-153 1996.75,-147 1996.75,-147 1996.75,-121 1996.75,-121 1996.75,-115 2002.75,-109 2008.75,-109 2008.75,-109 2108,-109 2108,-109 2114,-109 2120,-115 2120,-121 2120,-121 2120,-147 2120,-147 2120,-153 2114,-159 2108,-159"/>
|
||||||
d="M2108,-159C2108,-159 2008.75,-159 2008.75,-159 2002.75,-159 1996.75,-153 1996.75,-147 1996.75,-147 1996.75,-121 1996.75,-121 1996.75,-115 2002.75,-109 2008.75,-109 2008.75,-109 2108,-109 2108,-109 2114,-109 2120,-115 2120,-121 2120,-121 2120,-147 2120,-147 2120,-153 2114,-159 2108,-159"/>
|
<text text-anchor="middle" x="2058.38" y="-141.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">pst::encode_store</text>
|
||||||
<text text-anchor="middle" x="2058.38" y="-141.7" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="middle" x="2058.38" y="-120.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">-> bytes</text>
|
||||||
font-size="14.00">pst::encode_store
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="2058.38" y="-120.7" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">-> bytes
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- dump_before->encode -->
|
<!-- dump_before->encode -->
|
||||||
<g id="edge9" class="edge">
|
<g id="edge9" class="edge">
|
||||||
<title>dump_before->encode</title>
|
<title>dump_before->encode</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M1929.92,-134C1947.11,-134 1966.52,-134 1984.92,-134"/>
|
||||||
d="M1929.92,-134C1947.11,-134 1966.52,-134 1984.92,-134"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="1984.7,-137.5 1994.7,-134 1984.7,-130.5 1984.7,-137.5"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
|
||||||
points="1984.7,-137.5 1994.7,-134 1984.7,-130.5 1984.7,-137.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- compare -->
|
<!-- compare -->
|
||||||
<g id="node12" class="node">
|
<g id="node12" class="node">
|
||||||
<title>compare</title>
|
<title>compare</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M2540.75,-224C2540.75,-224 2425,-224 2425,-224 2419,-224 2413,-218 2413,-212 2413,-212 2413,-200 2413,-200 2413,-194 2419,-188 2425,-188 2425,-188 2540.75,-188 2540.75,-188 2546.75,-188 2552.75,-194 2552.75,-200 2552.75,-200 2552.75,-212 2552.75,-212 2552.75,-218 2546.75,-224 2540.75,-224"/>
|
||||||
d="M2540.75,-224C2540.75,-224 2425,-224 2425,-224 2419,-224 2413,-218 2413,-212 2413,-212 2413,-200 2413,-200 2413,-194 2419,-188 2425,-188 2425,-188 2540.75,-188 2540.75,-188 2546.75,-188 2552.75,-194 2552.75,-200 2552.75,-200 2552.75,-212 2552.75,-212 2552.75,-218 2546.75,-224 2540.75,-224"/>
|
<text text-anchor="middle" x="2482.88" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">dump equality check</text>
|
||||||
<text text-anchor="middle" x="2482.88" y="-203.2" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">dump equality check
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- dump_before->compare -->
|
<!-- dump_before->compare -->
|
||||||
<g id="edge12" class="edge">
|
<g id="edge12" class="edge">
|
||||||
<title>dump_before->compare</title>
|
<title>dump_before->compare</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" stroke-dasharray="5,2" d="M1927.2,-159.48C1948.13,-168.95 1973.07,-178.68 1996.75,-184 2136.12,-215.31 2303.52,-214.81 2400.78,-210.9"/>
|
||||||
d="M1927.2,-159.48C1948.13,-168.95 1973.07,-178.68 1996.75,-184 2136.12,-215.31 2303.52,-214.81 2400.78,-210.9"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="2400.93,-214.39 2410.77,-210.47 2400.63,-207.4 2400.93,-214.39"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
<text text-anchor="middle" x="2171.38" y="-214.27" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">expected</text>
|
||||||
points="2400.93,-214.39 2410.77,-210.47 2400.63,-207.4 2400.93,-214.39"/>
|
|
||||||
<text text-anchor="middle" x="2171.38" y="-214.27" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">expected
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- decode -->
|
<!-- decode -->
|
||||||
<g id="node11" class="node">
|
<g id="node11" class="node">
|
||||||
<title>decode</title>
|
<title>decode</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M2334,-159C2334,-159 2234.75,-159 2234.75,-159 2228.75,-159 2222.75,-153 2222.75,-147 2222.75,-147 2222.75,-121 2222.75,-121 2222.75,-115 2228.75,-109 2234.75,-109 2234.75,-109 2334,-109 2334,-109 2340,-109 2346,-115 2346,-121 2346,-121 2346,-147 2346,-147 2346,-153 2340,-159 2334,-159"/>
|
||||||
d="M2334,-159C2334,-159 2234.75,-159 2234.75,-159 2228.75,-159 2222.75,-153 2222.75,-147 2222.75,-147 2222.75,-121 2222.75,-121 2222.75,-115 2228.75,-109 2234.75,-109 2234.75,-109 2334,-109 2334,-109 2340,-109 2346,-115 2346,-121 2346,-121 2346,-147 2346,-147 2346,-153 2340,-159 2334,-159"/>
|
<text text-anchor="middle" x="2284.38" y="-141.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">pst::decode_store</text>
|
||||||
<text text-anchor="middle" x="2284.38" y="-141.7" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="middle" x="2284.38" y="-120.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">-> restored Store</text>
|
||||||
font-size="14.00">pst::decode_store
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="2284.38" y="-120.7" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">-> restored Store
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- encode->decode -->
|
<!-- encode->decode -->
|
||||||
<g id="edge10" class="edge">
|
<g id="edge10" class="edge">
|
||||||
<title>encode->decode</title>
|
<title>encode->decode</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M2120.29,-134C2148.18,-134 2181.46,-134 2210.66,-134"/>
|
||||||
d="M2120.29,-134C2148.18,-134 2181.46,-134 2210.66,-134"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="2210.66,-137.5 2220.66,-134 2210.66,-130.5 2210.66,-137.5"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
<text text-anchor="middle" x="2171.38" y="-138.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">bytes</text>
|
||||||
points="2210.66,-137.5 2220.66,-134 2210.66,-130.5 2210.66,-137.5"/>
|
|
||||||
<text text-anchor="middle" x="2171.38" y="-138.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">bytes
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- decode->compare -->
|
<!-- decode->compare -->
|
||||||
<g id="edge11" class="edge">
|
<g id="edge11" class="edge">
|
||||||
<title>decode->compare</title>
|
<title>decode->compare</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M2346.47,-156.39C2370.11,-165.05 2397.1,-174.94 2420.74,-183.6"/>
|
||||||
d="M2346.47,-156.39C2370.11,-165.05 2397.1,-174.94 2420.74,-183.6"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="2419.39,-186.83 2429.99,-186.99 2421.8,-180.26 2419.39,-186.83"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
|
||||||
points="2419.39,-186.83 2429.99,-186.99 2421.8,-180.26 2419.39,-186.83"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- compare->demo_report -->
|
<!-- compare->demo_report -->
|
||||||
<g id="edge13" class="edge">
|
<g id="edge13" class="edge">
|
||||||
<title>compare->demo_report</title>
|
<title>compare->demo_report</title>
|
||||||
<path fill="none" stroke="#607d8b" stroke-width="1.2"
|
<path fill="none" stroke="#607d8b" stroke-width="1.2" d="M2553.15,-223.85C2573.13,-229 2595.42,-234.74 2617.13,-240.34"/>
|
||||||
d="M2553.15,-223.85C2573.13,-229 2595.42,-234.74 2617.13,-240.34"/>
|
<polygon fill="#607d8b" stroke="#607d8b" stroke-width="1.2" points="2616.11,-243.69 2626.67,-242.79 2617.85,-236.91 2616.11,-243.69"/>
|
||||||
<polygon fill="#607d8b" stroke="#607d8b" stroke-width="1.2"
|
|
||||||
points="2616.11,-243.69 2626.67,-242.79 2617.85,-236.91 2616.11,-243.69"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- stdout -->
|
<!-- stdout -->
|
||||||
<g id="node14" class="node">
|
<g id="node14" class="node">
|
||||||
<title>stdout</title>
|
<title>stdout</title>
|
||||||
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5"
|
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5" d="M2974.25,-293C2974.25,-293 2901.25,-293 2901.25,-293 2895.25,-293 2889.25,-287 2889.25,-281 2889.25,-281 2889.25,-255 2889.25,-255 2889.25,-249 2895.25,-243 2901.25,-243 2901.25,-243 2974.25,-243 2974.25,-243 2980.25,-243 2986.25,-249 2986.25,-255 2986.25,-255 2986.25,-281 2986.25,-281 2986.25,-287 2980.25,-293 2974.25,-293"/>
|
||||||
d="M2974.25,-293C2974.25,-293 2901.25,-293 2901.25,-293 2895.25,-293 2889.25,-287 2889.25,-281 2889.25,-281 2889.25,-255 2889.25,-255 2889.25,-249 2895.25,-243 2901.25,-243 2901.25,-243 2974.25,-243 2974.25,-243 2980.25,-243 2986.25,-249 2986.25,-255 2986.25,-255 2986.25,-281 2986.25,-281 2986.25,-287 2980.25,-293 2974.25,-293"/>
|
<text text-anchor="middle" x="2937.75" y="-275.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">stdout</text>
|
||||||
<text text-anchor="middle" x="2937.75" y="-275.7" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="middle" x="2937.75" y="-254.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">(println! lines)</text>
|
||||||
font-size="14.00">stdout
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="2937.75" y="-254.7" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">(println! lines)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- demo_report->stdout -->
|
<!-- demo_report->stdout -->
|
||||||
<g id="edge15" class="edge">
|
<g id="edge15" class="edge">
|
||||||
<title>demo_report->stdout</title>
|
<title>demo_report->stdout</title>
|
||||||
<path fill="none" stroke="#607d8b" stroke-width="1.2"
|
<path fill="none" stroke="#607d8b" stroke-width="1.2" d="M2822.7,-268C2841.33,-268 2860.33,-268 2877.35,-268"/>
|
||||||
d="M2822.7,-268C2841.33,-268 2860.33,-268 2877.35,-268"/>
|
<polygon fill="#607d8b" stroke="#607d8b" stroke-width="1.2" points="2876.96,-271.5 2886.96,-268 2876.96,-264.5 2876.96,-271.5"/>
|
||||||
<polygon fill="#607d8b" stroke="#607d8b" stroke-width="1.2"
|
|
||||||
points="2876.96,-271.5 2886.96,-268 2876.96,-264.5 2876.96,-271.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 20 KiB |
@ -129,7 +129,7 @@ fn row_count(storage: &GeomergeStorage, table: &str) -> Result<usize, StorageErr
|
|||||||
#[allow(clippy::expect_used, clippy::unwrap_used)]
|
#[allow(clippy::expect_used, clippy::unwrap_used)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
GeomergeStorage, Storage, Value, add_paths_data, load_paths_theory, row_count, run_demo,
|
add_paths_data, load_paths_theory, row_count, run_demo, GeomergeStorage, Storage, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -1,166 +0,0 @@
|
|||||||
{
|
|
||||||
"_scenario": "three-atom-chain",
|
|
||||||
"facts": {
|
|
||||||
"edge": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"str": "node:1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"str": "node:2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"str": "edge:1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"str": "node:2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"str": "node:3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"str": "edge:2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"node": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"str": "node:1"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"str": "node:2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"str": "node:3"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"query": {
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"scan": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"var": "a"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"var": "b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"var": "_w0_2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"table": "edge"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"scan": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"var": "b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"var": "c"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"var": "_w1_2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"table": "edge"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"scan": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"var": "a"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"table": "node"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"join": {
|
|
||||||
"left": 1,
|
|
||||||
"op": "left",
|
|
||||||
"right": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"join": {
|
|
||||||
"left": 2,
|
|
||||||
"op": "left",
|
|
||||||
"right": 4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"join": {
|
|
||||||
"left": 5,
|
|
||||||
"op": "right",
|
|
||||||
"right": 4
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"join": {
|
|
||||||
"left": 6,
|
|
||||||
"op": "right",
|
|
||||||
"right": 3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"join": {
|
|
||||||
"left": 6,
|
|
||||||
"op": "natural",
|
|
||||||
"right": 7
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"action": {
|
|
||||||
"join": {
|
|
||||||
"left": 5,
|
|
||||||
"op": "natural",
|
|
||||||
"right": 8
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": 9
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"root": 9
|
|
||||||
},
|
|
||||||
"schema": {
|
|
||||||
"edge": 3,
|
|
||||||
"node": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,344 +0,0 @@
|
|||||||
//! End-to-end runner that executes a `geolog-lang` conjunctive-query plan
|
|
||||||
//! against this workspace's storage and `query-ops` operators.
|
|
||||||
//!
|
|
||||||
//! The upstream Haskell planner in `external/geolog/geolog-lang`
|
|
||||||
//! (`Geolog.DB.Plan`) builds a Yannakakis-style join DAG over `QAtom`s. This
|
|
||||||
//! crate accepts that DAG as JSON, materializes the input relations through
|
|
||||||
//! the [`Storage`] trait, and walks the DAG using
|
|
||||||
//! [`query_ops::atom::scan_atom`], [`query_ops::join::semijoin`], and
|
|
||||||
//! [`query_ops::join::natural_join`]. The result is a binding
|
|
||||||
//! [`Relation`](query_ops::relation::Relation) over the query's variables.
|
|
||||||
//!
|
|
||||||
//! The JSON IR mirrors `Geolog.DB.Plan.JoinPlan` and `Geolog.DB.InMemory.QAtom`
|
|
||||||
//! without depending on the Haskell side at build time. A Haskell exporter
|
|
||||||
//! that dumps `(schema, facts, JoinPlan)` to this shape is the planned
|
|
||||||
//! follow-up that completes the round trip; the IR is the contract.
|
|
||||||
//!
|
|
||||||
//! Mapping from the Haskell planner:
|
|
||||||
//!
|
|
||||||
//! | `Geolog.DB.Plan` | this crate |
|
|
||||||
//! |-----------------------------|-----------------------------------------------|
|
|
||||||
//! | `PlanEvalAtom` | [`Action::Scan`] → `scan_atom` |
|
|
||||||
//! | `PlanJoin LeftJoin a b` | [`Action::Join`] with [`JoinOp::Left`] → `semijoin(rel[a], rel[b])` |
|
|
||||||
//! | `PlanJoin RightJoin a b` | [`Action::Join`] with [`JoinOp::Right`] → `semijoin(rel[b], rel[a])` |
|
|
||||||
//! | `PlanJoin NaturalJoin a b` | [`Action::Join`] with [`JoinOp::Natural`] → `natural_join(rel[a], rel[b])` |
|
|
||||||
//!
|
|
||||||
//! The atom side covers `evalAtom` (`Geolog.DB.InMemory`): a [`Term::Var`]
|
|
||||||
//! repeated across positions enforces equality, [`Term::Lit`] filters by
|
|
||||||
//! constant, and distinct variables project in first-occurrence order.
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
|
||||||
use query_ops::join::{natural_join, semijoin};
|
|
||||||
use query_ops::relation::Relation;
|
|
||||||
use storage::value::Value;
|
|
||||||
use storage::{MemoryStorage, Storage, StorageError, scan_as_table};
|
|
||||||
|
|
||||||
/// A single fixture: schema, ground facts, and a query plan to execute.
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct Plan {
|
|
||||||
/// Relation name → arity (column count).
|
|
||||||
pub schema: HashMap<String, usize>,
|
|
||||||
/// Relation name → list of ground tuples to insert before execution.
|
|
||||||
pub facts: HashMap<String, Vec<Vec<JsonValue>>>,
|
|
||||||
/// The join DAG itself.
|
|
||||||
pub query: Query,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mirrors `Geolog.DB.Plan.JoinPlan`: a set of nodes plus the id of the
|
|
||||||
/// rooted result node.
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct Query {
|
|
||||||
pub root: u32,
|
|
||||||
pub nodes: Vec<Node>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// One node of the plan DAG. `id`s are dense within a `Query` but don't need
|
|
||||||
/// to start at any particular value, mirroring the Haskell `PlanNodeId`.
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct Node {
|
|
||||||
pub id: u32,
|
|
||||||
pub action: Action,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// What to compute at a node. Tagged externally so JSON reads as
|
|
||||||
/// `{"action": {"scan": {...}}}` or `{"action": {"join": {...}}}`.
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Action {
|
|
||||||
Scan(Atom),
|
|
||||||
Join(Join),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A flat atom pattern, one entry per column of the target relation.
|
|
||||||
/// Matches the `toFlatArgs` view used by `Geolog.DB.InMemory.evalAtom`:
|
|
||||||
/// `qaValues` positions are filled in directly, and the entity-id column
|
|
||||||
/// (if any) appears at the last position. Wildcard positions in the
|
|
||||||
/// Haskell `QAtom` (a `Map Int QVal` with a missing key) translate to a
|
|
||||||
/// fresh, unique variable name on this side, which the operator binds but
|
|
||||||
/// never joins against.
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct Atom {
|
|
||||||
pub table: String,
|
|
||||||
pub columns: Vec<JsonTerm>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum JsonTerm {
|
|
||||||
Var(String),
|
|
||||||
Lit(JsonValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wire-level value tag. Restricted to what `storage::value::Value` carries.
|
|
||||||
/// Entity identities from the Haskell side (`ValEntity path id`) round-trip
|
|
||||||
/// through `Str` for now using a `"path:id"` convention; that's a fixture
|
|
||||||
/// concern, not a runner concern.
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum JsonValue {
|
|
||||||
Int(i64),
|
|
||||||
Str(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct Join {
|
|
||||||
pub op: JoinOp,
|
|
||||||
pub left: u32,
|
|
||||||
pub right: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum JoinOp {
|
|
||||||
/// `Geolog.DB.Plan.LeftJoin`: result is `left` rows whose shared columns
|
|
||||||
/// appear in `right`. Lowered to `semijoin(left, right)`.
|
|
||||||
Left,
|
|
||||||
/// `Geolog.DB.Plan.RightJoin`: result is `right` rows whose shared
|
|
||||||
/// columns appear in `left`. Lowered to `semijoin(right, left)`.
|
|
||||||
Right,
|
|
||||||
/// `Geolog.DB.Plan.NaturalJoin`. Lowered to `natural_join(left, right)`.
|
|
||||||
Natural,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors a runner can produce in addition to storage failures.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum RunError {
|
|
||||||
/// A fact references a relation that isn't declared in `schema`.
|
|
||||||
UnknownRelation(String),
|
|
||||||
/// A node id appears in a `Join` action but no node with that id exists.
|
|
||||||
MissingNode(u32),
|
|
||||||
/// `Query.root` doesn't match any node in `nodes`.
|
|
||||||
MissingRoot(u32),
|
|
||||||
/// Two nodes share the same id.
|
|
||||||
DuplicateNode(u32),
|
|
||||||
/// A join node references its left or right side before that side has
|
|
||||||
/// been computed: the DAG isn't actually topologically sorted by id, or
|
|
||||||
/// it has a cycle.
|
|
||||||
UnresolvedDependency { node: u32, depends_on: u32 },
|
|
||||||
/// Storage layer rejected an operation.
|
|
||||||
Storage(StorageError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for RunError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::UnknownRelation(name) => {
|
|
||||||
write!(f, "facts reference relation {name:?} not in schema")
|
|
||||||
}
|
|
||||||
Self::MissingNode(id) => write!(f, "plan references missing node id {id}"),
|
|
||||||
Self::MissingRoot(id) => write!(f, "plan root id {id} matches no node"),
|
|
||||||
Self::DuplicateNode(id) => write!(f, "duplicate node id {id} in plan"),
|
|
||||||
Self::UnresolvedDependency { node, depends_on } => write!(
|
|
||||||
f,
|
|
||||||
"node {node} depends on {depends_on}, which has not been computed yet"
|
|
||||||
),
|
|
||||||
Self::Storage(err) => write!(f, "storage error: {err}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for RunError {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
match self {
|
|
||||||
Self::Storage(err) => Some(err),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<StorageError> for RunError {
|
|
||||||
fn from(err: StorageError) -> Self {
|
|
||||||
Self::Storage(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<JsonValue> for Value {
|
|
||||||
fn from(jv: JsonValue) -> Self {
|
|
||||||
match jv {
|
|
||||||
JsonValue::Int(n) => Self::Int(n),
|
|
||||||
JsonValue::Str(s) => Self::Str(s),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<JsonTerm> for Term {
|
|
||||||
fn from(t: JsonTerm) -> Self {
|
|
||||||
match t {
|
|
||||||
JsonTerm::Var(name) => Self::Var(name),
|
|
||||||
JsonTerm::Lit(value) => Self::Lit(value.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a [`Plan`] from a JSON string.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns a [`serde_json::Error`] if the input isn't valid JSON in the
|
|
||||||
/// expected shape.
|
|
||||||
pub fn parse_plan(json: &str) -> Result<Plan, serde_json::Error> {
|
|
||||||
serde_json::from_str(json)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load schema and facts from a [`Plan`] into a fresh [`MemoryStorage`].
|
|
||||||
///
|
|
||||||
/// All facts are inserted in a single transaction; commit is atomic so a
|
|
||||||
/// failure on row N leaves the storage empty.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns [`RunError::UnknownRelation`] if facts mention a relation not
|
|
||||||
/// declared in `schema`. Wraps storage failures (arity mismatch, transaction
|
|
||||||
/// errors) in [`RunError::Storage`].
|
|
||||||
pub fn load_into_memory(plan: &Plan) -> Result<MemoryStorage, RunError> {
|
|
||||||
let mut storage = MemoryStorage::default();
|
|
||||||
for (name, arity) in &plan.schema {
|
|
||||||
storage.create_relation(name, *arity)?;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let mut tx = storage.transaction()?;
|
|
||||||
for (name, rows) in &plan.facts {
|
|
||||||
if !plan.schema.contains_key(name) {
|
|
||||||
return Err(RunError::UnknownRelation(name.clone()));
|
|
||||||
}
|
|
||||||
for row in rows {
|
|
||||||
let cells: Vec<Value> = row.iter().cloned().map(Value::from).collect();
|
|
||||||
tx.insert(name, cells)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = tx.commit()?;
|
|
||||||
}
|
|
||||||
Ok(storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute a plan against a storage backend, returning the bindings
|
|
||||||
/// [`Relation`] for the rooted plan node.
|
|
||||||
///
|
|
||||||
/// Nodes are executed in ascending `id` order. For a Yannakakis plan as
|
|
||||||
/// emitted by `Geolog.DB.Plan` this is equivalent to a topological sort,
|
|
||||||
/// since `insertJoin` only references node ids that have already been
|
|
||||||
/// allocated. A non-monotone id ordering is rejected with
|
|
||||||
/// [`RunError::UnresolvedDependency`].
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns [`RunError::DuplicateNode`] for repeated ids,
|
|
||||||
/// [`RunError::MissingNode`] for join references to unknown ids,
|
|
||||||
/// [`RunError::MissingRoot`] if `query.root` isn't present, and storage
|
|
||||||
/// errors during the per-scan `scan_as_table` call.
|
|
||||||
pub fn execute<S: Storage>(storage: &S, query: &Query) -> Result<Relation, RunError> {
|
|
||||||
let mut seen_ids: std::collections::HashSet<u32> =
|
|
||||||
std::collections::HashSet::with_capacity(query.nodes.len());
|
|
||||||
for node in &query.nodes {
|
|
||||||
if !seen_ids.insert(node.id) {
|
|
||||||
return Err(RunError::DuplicateNode(node.id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !seen_ids.contains(&query.root) {
|
|
||||||
return Err(RunError::MissingRoot(query.root));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ordered: Vec<&Node> = query.nodes.iter().collect();
|
|
||||||
ordered.sort_by_key(|n| n.id);
|
|
||||||
|
|
||||||
let mut results: HashMap<u32, Relation> = HashMap::with_capacity(ordered.len());
|
|
||||||
for node in ordered {
|
|
||||||
let computed = match &node.action {
|
|
||||||
Action::Scan(atom) => {
|
|
||||||
let table = scan_as_table(storage, &atom.table)?;
|
|
||||||
let pattern = AtomPattern {
|
|
||||||
columns: atom.columns.iter().cloned().map(Term::from).collect(),
|
|
||||||
};
|
|
||||||
scan_atom(&table, &pattern)
|
|
||||||
}
|
|
||||||
Action::Join(join) => {
|
|
||||||
let left = require_dep(&results, &seen_ids, node.id, join.left)?;
|
|
||||||
let right = require_dep(&results, &seen_ids, node.id, join.right)?;
|
|
||||||
match join.op {
|
|
||||||
JoinOp::Left => semijoin(left, right),
|
|
||||||
JoinOp::Right => semijoin(right, left),
|
|
||||||
JoinOp::Natural => natural_join(left, right),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
results.insert(node.id, computed);
|
|
||||||
}
|
|
||||||
|
|
||||||
results
|
|
||||||
.remove(&query.root)
|
|
||||||
.ok_or(RunError::MissingRoot(query.root))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn require_dep<'a>(
|
|
||||||
results: &'a HashMap<u32, Relation>,
|
|
||||||
seen: &std::collections::HashSet<u32>,
|
|
||||||
node: u32,
|
|
||||||
depends_on: u32,
|
|
||||||
) -> Result<&'a Relation, RunError> {
|
|
||||||
if let Some(rel) = results.get(&depends_on) {
|
|
||||||
Ok(rel)
|
|
||||||
} else if seen.contains(&depends_on) {
|
|
||||||
Err(RunError::UnresolvedDependency { node, depends_on })
|
|
||||||
} else {
|
|
||||||
Err(RunError::MissingNode(depends_on))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience: parse JSON, load it into a fresh in-memory storage, and
|
|
||||||
/// execute, returning the root binding relation.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns a JSON parse error if the input is malformed, or a [`RunError`]
|
|
||||||
/// for any later step.
|
|
||||||
pub fn run_json(json: &str) -> Result<Relation, RunFromJsonError> {
|
|
||||||
let plan = parse_plan(json).map_err(RunFromJsonError::Parse)?;
|
|
||||||
let storage = load_into_memory(&plan).map_err(RunFromJsonError::Run)?;
|
|
||||||
let bindings = execute(&storage, &plan.query).map_err(RunFromJsonError::Run)?;
|
|
||||||
Ok(bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Combined error from [`run_json`].
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum RunFromJsonError {
|
|
||||||
Parse(serde_json::Error),
|
|
||||||
Run(RunError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for RunFromJsonError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Parse(err) => write!(f, "parse error: {err}"),
|
|
||||||
Self::Run(err) => write!(f, "run error: {err}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for RunFromJsonError {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
match self {
|
|
||||||
Self::Parse(err) => Some(err),
|
|
||||||
Self::Run(err) => Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
//! `glog-run` CLI: read a JSON plan from a file (or stdin if `-`), execute
|
|
||||||
//! it against a fresh in-memory store, and print the resulting binding
|
|
||||||
//! relation as JSON on stdout.
|
|
||||||
|
|
||||||
use std::io::{self, Read};
|
|
||||||
use std::process::ExitCode;
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
|
||||||
let mut args = std::env::args().skip(1);
|
|
||||||
let Some(path) = args.next() else {
|
|
||||||
eprintln!("usage: glog-run <plan.json | ->");
|
|
||||||
return ExitCode::from(2);
|
|
||||||
};
|
|
||||||
|
|
||||||
let input = match read_input(&path) {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("failed to read {path}: {err}");
|
|
||||||
return ExitCode::from(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let relation = match glog_runner::run_json(&input) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("{err}");
|
|
||||||
return ExitCode::from(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let payload = serde_json::json!({
|
|
||||||
"columns": relation.columns,
|
|
||||||
"rows": relation
|
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|row| row.iter().map(value_to_json).collect::<Vec<_>>())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
});
|
|
||||||
println!("{payload}");
|
|
||||||
ExitCode::SUCCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_input(path: &str) -> io::Result<String> {
|
|
||||||
if path == "-" {
|
|
||||||
let mut buf = String::new();
|
|
||||||
io::stdin().read_to_string(&mut buf)?;
|
|
||||||
Ok(buf)
|
|
||||||
} else {
|
|
||||||
std::fs::read_to_string(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn value_to_json(value: &storage::value::Value) -> serde_json::Value {
|
|
||||||
match value {
|
|
||||||
storage::value::Value::Int(n) => serde_json::Value::Number((*n).into()),
|
|
||||||
storage::value::Value::Str(s) => serde_json::Value::String(s.clone()),
|
|
||||||
storage::value::Value::Id(id) => serde_json::Value::String(id.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
//! End-to-end check: run the JSON fixture and verify the resulting bindings
|
|
||||||
//! match the `DB.InMemoryTest` "matches evalConjunction on three-atom chain"
|
|
||||||
//! case from `external/geolog/geolog-lang/test/DB/InMemoryTest.hs`.
|
|
||||||
//!
|
|
||||||
//! For `node = {e1, e2, e3}` and `edge = {(e1,e2,ee1), (e2,e3,ee2)}` the
|
|
||||||
//! conjunction `node(a), edge(a, b, _), edge(b, c, _)` has exactly one
|
|
||||||
//! solution: `(a=e1, b=e2, c=e3)`.
|
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use glog_runner::run_json;
|
|
||||||
use storage::value::Value;
|
|
||||||
|
|
||||||
fn fixture() -> &'static str {
|
|
||||||
include_str!("../fixtures/three_atom_chain.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ent(path: &str, id: u32) -> Value {
|
|
||||||
Value::Str(format!("{path}:{id}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn project<'a>(
|
|
||||||
columns: &'a [String],
|
|
||||||
row: &'a [Value],
|
|
||||||
keep: &'a [&'a str],
|
|
||||||
) -> BTreeMap<&'a str, &'a Value> {
|
|
||||||
keep.iter()
|
|
||||||
.map(|name| {
|
|
||||||
let pos = columns
|
|
||||||
.iter()
|
|
||||||
.position(|c| c == name)
|
|
||||||
.expect("column missing");
|
|
||||||
(*name, &row[pos])
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn three_atom_chain_matches_haskell_oracle() {
|
|
||||||
let result = run_json(fixture()).expect("fixture should execute");
|
|
||||||
|
|
||||||
// The plan's root keeps every variable, including the per-atom wildcards
|
|
||||||
// `_r1` and `_r2`. The oracle only asserts the (a, b, c) projection.
|
|
||||||
let keep = ["a", "b", "c"];
|
|
||||||
let mut projected: Vec<BTreeMap<&str, &Value>> = result
|
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.map(|row| project(&result.columns, row, &keep))
|
|
||||||
.collect();
|
|
||||||
projected.sort_by_key(|m| format!("{m:?}"));
|
|
||||||
|
|
||||||
let e1 = ent("node", 1);
|
|
||||||
let e2 = ent("node", 2);
|
|
||||||
let e3 = ent("node", 3);
|
|
||||||
let expected = vec![BTreeMap::from([("a", &e1), ("b", &e2), ("c", &e3)])];
|
|
||||||
|
|
||||||
assert_eq!(projected, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn root_columns_cover_a_b_c_plus_two_wildcards() {
|
|
||||||
// The exporter emits unique wildcard variable names for the entity-id
|
|
||||||
// column of each edge atom (e.g. `_w0_2`, `_w1_2`); their exact spelling
|
|
||||||
// is an implementation detail of the exporter, so this test only checks
|
|
||||||
// that the named variables are all present and that the total column
|
|
||||||
// count is the three named ones plus two anonymous wildcards.
|
|
||||||
let result = run_json(fixture()).expect("fixture should execute");
|
|
||||||
let cols: std::collections::HashSet<&str> = result.columns.iter().map(String::as_str).collect();
|
|
||||||
for expected in ["a", "b", "c"] {
|
|
||||||
assert!(cols.contains(expected), "missing column {expected}");
|
|
||||||
}
|
|
||||||
assert_eq!(result.columns.len(), 5, "expected 3 named + 2 wildcards");
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "glog-runner"
|
name = "plan-runner"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
@ -9,11 +9,18 @@ rust-version.workspace = true
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
storage = { path = "../storage" }
|
|
||||||
query-ops = { path = "../query-ops" }
|
query-ops = { path = "../query-ops" }
|
||||||
|
storage = { path = "../storage", features = [
|
||||||
|
"lmdb",
|
||||||
|
"redb",
|
||||||
|
"fjall",
|
||||||
|
"sqlite",
|
||||||
|
"geomerge",
|
||||||
|
] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "glog-run"
|
name = "plan-run"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
122
crates/plan-runner/README.md
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
## Plan Runner
|
||||||
|
|
||||||
|
This crate is a snapshot executor for conjunctive-query plans.
|
||||||
|
It reads a JSON plan (a DAG of scan and join nodes plus the input facts),
|
||||||
|
walks the DAG using the operators from [`query-ops`](../query-ops),
|
||||||
|
and prints the binding relation produced at the root node.
|
||||||
|
|
||||||
|
The wire format mirrors `Geolog.DB.Plan.PlanGraph` from the
|
||||||
|
[`geolog`](../../external/geolog) submodule, but the JSON shape is the contract:
|
||||||
|
any frontend that emits this format can drive the runner.
|
||||||
|
The mapping from `PlanEvalAtom` / `PlanJoin` to `scan_atom` / `semijoin` / `natural_join`,
|
||||||
|
and the full IR spec, are documented as module-level rustdoc in
|
||||||
|
[`src/lib.rs`](src/lib.rs).
|
||||||
|
|
||||||
|
### Pipeline
|
||||||
|
|
||||||
|
End-to-end, scenarios become runner output through three stages:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tools/exporter/examples/*.scenario.json
|
||||||
|
└── (Haskell exporter; runs Geolog.DB.Plan.planConjunction
|
||||||
|
and Geolog.DB.InMemory.evalConjunctionPlanned as a self-check)
|
||||||
|
└── crates/plan-runner/fixtures/*.json (JSON IR; checked in)
|
||||||
|
└── (plan-runner; this crate)
|
||||||
|
└── stdout JSON, with row-for-row oracle check
|
||||||
|
```
|
||||||
|
|
||||||
|
The exporter (`tools/exporter`) is the only producer of runner IR today;
|
||||||
|
it's where atoms are planned and rejected if they don't fit the supported subset.
|
||||||
|
Fixtures are regenerated with `make export-fixtures`, and the full loop is `make examples`.
|
||||||
|
|
||||||
|
### Backends
|
||||||
|
|
||||||
|
The CLI takes a `--backend` flag.
|
||||||
|
The `memory` backend is the pure in-memory path;
|
||||||
|
every other backend routes facts through the [`Storage`](../storage) trait
|
||||||
|
via `build_tables_via_storage`, then scans tables back out before executing.
|
||||||
|
|
||||||
|
| Backend | Storage | Location |
|
||||||
|
|------------------|------------------------------------------------|-----------------------|
|
||||||
|
| `memory` | none (direct from `plan.facts`) | n/a |
|
||||||
|
| `memory-storage` | `MemoryStorage` | in-process |
|
||||||
|
| `lmdb` | `LmdbStorage` (heed-backed mmap B-tree) | fresh tempdir per run |
|
||||||
|
| `redb` | `RedbStorage` (single-file B-tree) | fresh tempdir per run |
|
||||||
|
| `fjall` | `FjallStorage` (LSM tree) | fresh tempdir per run |
|
||||||
|
| `sqlite` | `SqliteStorage` (rusqlite, bundled libsqlite3) | fresh tempdir per run |
|
||||||
|
| `geomerge` | `GeomergeStorage` (CRDT; alpha) | in-process |
|
||||||
|
|
||||||
|
All seven produce byte-identical output for every checked-in fixture.
|
||||||
|
The point of the abstraction is not performance comparison
|
||||||
|
(the snapshot evaluator is bulk-materialized either way),
|
||||||
|
but to validate that the storage layer is genuinely backend-neutral
|
||||||
|
and that adding a new adapter is a constructor swap.
|
||||||
|
|
||||||
|
Note on `geomerge`:
|
||||||
|
the runner's JSON IR is untyped (only arity per relation),
|
||||||
|
but geomerge requires a typed theory upfront.
|
||||||
|
The CLI infers column types from the first fact row per relation
|
||||||
|
and synthesizes a theory of `PrimInt` and `PrimString` columns via
|
||||||
|
[`GeomergeStorage::with_relations`](../storage/src/adapters/geomerge.rs).
|
||||||
|
Columns with no sample facts default to `PrimString`.
|
||||||
|
|
||||||
|
### Run It
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Run one fixture through the default in-memory path:
|
||||||
|
cargo run -p plan-runner -- crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
|
||||||
|
# Same plan, routed through different backends:
|
||||||
|
cargo run -p plan-runner -- --backend memory-storage crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
cargo run -p plan-runner -- --backend lmdb crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
cargo run -p plan-runner -- --backend redb crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
cargo run -p plan-runner -- --backend fjall crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
cargo run -p plan-runner -- --backend sqlite crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
cargo run -p plan-runner -- --backend geomerge crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
|
||||||
|
# Regenerate every fixture from its scenario and run the oracle test:
|
||||||
|
make examples
|
||||||
|
```
|
||||||
|
|
||||||
|
A sample run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ plan-run crates/plan-runner/fixtures/two_atom_join.json
|
||||||
|
{"columns":["a","b","_w0_2"],"rows":[["node:1","node:2","edge:1"],["node:2","node:1","edge:2"]]}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `_w<atomIdx>_<pos>` columns are wildcards the exporter named so the runner can bind them.
|
||||||
|
The scenario's `expected_bindings` block names only the variables the test cares about,
|
||||||
|
and `verify` projects the runner output to that subset before comparing as a multiset.
|
||||||
|
|
||||||
|
### Run the Tests
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo test -p plan-runner
|
||||||
|
```
|
||||||
|
|
||||||
|
The two integration test files exercise complementary properties:
|
||||||
|
|
||||||
|
- `tests/examples.rs` walks every fixture and checks it against its `expected_bindings` oracle.
|
||||||
|
- `tests/storage_roundtrip.rs` cross-checks the pure path against the storage-backed path,
|
||||||
|
to keep `build_tables` and `build_tables_via_storage` in lockstep.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- **IR contract.**
|
||||||
|
The runner is backend-agnostic and frontend-agnostic:
|
||||||
|
it consumes JSON in the shape documented in `src/lib.rs` and produces a binding relation.
|
||||||
|
Anything that emits the same JSON can drive it.
|
||||||
|
- **No optimizer.**
|
||||||
|
Plans are executed as written.
|
||||||
|
Node ordering, join shape, and antijoin scheduling are all the producer's responsibility.
|
||||||
|
This crate's job ends at faithful execution of the IR.
|
||||||
|
- **Wildcard columns survive.**
|
||||||
|
`scan_atom` keeps every distinct variable that appears in the pattern,
|
||||||
|
including the exporter's synthetic `_w<atomIdx>_<pos>` names.
|
||||||
|
The runner does not project them out;
|
||||||
|
oracle verification handles that on the comparison side.
|
||||||
|
- **Bulk, not streaming.**
|
||||||
|
Each node materializes its full output as a `Relation`.
|
||||||
|
This matches `query-ops`' execution model;
|
||||||
|
it's not designed for incremental or maintained-view workloads.
|
||||||
114
crates/plan-runner/fixtures/cartesian.json
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"_scenario": "cartesian",
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "left:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "right:10"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "left:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "right:20"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "left:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "right:10"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "left:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "right:20"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"left": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "left:1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "left:2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"right": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "right:10"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "right:20"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "left"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "b"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "right"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 1,
|
||||||
|
"op": "natural",
|
||||||
|
"right": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"root": 3
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"left": 1,
|
||||||
|
"right": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
84
crates/plan-runner/fixtures/self_loop.json
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"_scenario": "self-loop",
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"x"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"edge": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "_w0_2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "edge"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"root": 1
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"edge": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
186
crates/plan-runner/fixtures/three_atom_chain.json
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
{
|
||||||
|
"_scenario": "three-atom-chain",
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"edge": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"node": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "_w0_2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "edge"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "_w1_2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "edge"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 1,
|
||||||
|
"op": "left",
|
||||||
|
"right": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 2,
|
||||||
|
"op": "left",
|
||||||
|
"right": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 5,
|
||||||
|
"op": "right",
|
||||||
|
"right": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 6,
|
||||||
|
"op": "right",
|
||||||
|
"right": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 6,
|
||||||
|
"op": "natural",
|
||||||
|
"right": 7
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 5,
|
||||||
|
"op": "natural",
|
||||||
|
"right": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"root": 9
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"edge": 3,
|
||||||
|
"node": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
136
crates/plan-runner/fixtures/two_atom_join.json
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
{
|
||||||
|
"_scenario": "two-atom-join",
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"edge": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"str": "edge:2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"node": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"str": "node:2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"var": "_w0_2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "edge"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"scan": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"var": "a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"table": "node"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 1,
|
||||||
|
"op": "left",
|
||||||
|
"right": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 3,
|
||||||
|
"op": "right",
|
||||||
|
"right": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": {
|
||||||
|
"join": {
|
||||||
|
"left": 3,
|
||||||
|
"op": "natural",
|
||||||
|
"right": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"root": 5
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"edge": 3,
|
||||||
|
"node": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
540
crates/plan-runner/src/lib.rs
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
//! Snapshot executor for conjunctive-query plans.
|
||||||
|
//!
|
||||||
|
//! Takes a structural plan (a DAG of `Scan` and `Join` nodes), the input
|
||||||
|
//! tables it scans, and walks the DAG via [`query_ops::atom::scan_atom`],
|
||||||
|
//! [`query_ops::join::semijoin`], and [`query_ops::join::natural_join`].
|
||||||
|
//! The result is a binding [`Relation`](query_ops::relation::Relation) over
|
||||||
|
//! the query's variables.
|
||||||
|
//!
|
||||||
|
//! The runner is intentionally backend-agnostic: it depends only on
|
||||||
|
//! `query-ops`, and the planner that emits the JSON IR is decoupled from
|
||||||
|
//! the storage backend that produced the facts. To execute a plan against
|
||||||
|
//! a [`Storage`](storage::Storage) backend, materialize each input table
|
||||||
|
//! with [`storage::scan_as_table`] and call [`execute`] with the resulting
|
||||||
|
//! map. The in-tree `tests/storage_roundtrip.rs` is the canonical example.
|
||||||
|
//!
|
||||||
|
//! The JSON IR mirrors `Geolog.DB.Plan.PlanGraph` and
|
||||||
|
//! `Geolog.DB.InMemory.QAtom` from the `external/geolog` submodule, but the
|
||||||
|
//! shape is the contract: any frontend that emits this JSON can use the
|
||||||
|
//! runner.
|
||||||
|
//!
|
||||||
|
//! Operator mapping from the Haskell planner:
|
||||||
|
//!
|
||||||
|
//! | `Geolog.DB.Plan` | this crate |
|
||||||
|
//! |-----------------------------|-----------------------------------------------|
|
||||||
|
//! | `PlanEvalAtom` | [`Action::Scan`] → `scan_atom` |
|
||||||
|
//! | `PlanJoin LeftJoin a b` | [`Action::Join`] with [`JoinOp::Left`] → `semijoin(rel[a], rel[b])` |
|
||||||
|
//! | `PlanJoin RightJoin a b` | [`Action::Join`] with [`JoinOp::Right`] → `semijoin(rel[b], rel[a])` |
|
||||||
|
//! | `PlanJoin NaturalJoin a b` | [`Action::Join`] with [`JoinOp::Natural`] → `natural_join(rel[a], rel[b])` |
|
||||||
|
//!
|
||||||
|
//! The atom side covers `evalAtom` (`Geolog.DB.InMemory`): a [`Term::Var`]
|
||||||
|
//! repeated across positions enforces equality, [`Term::Lit`] filters by
|
||||||
|
//! constant, and distinct variables project in first-occurrence order.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use query_ops::atom::{scan_atom, AtomPattern, Term};
|
||||||
|
use query_ops::join::{natural_join, semijoin};
|
||||||
|
use query_ops::relation::Relation;
|
||||||
|
use storage::table::Table;
|
||||||
|
use storage::value::Value;
|
||||||
|
use storage::{scan_as_table, Storage, StorageError};
|
||||||
|
|
||||||
|
/// A single fixture: schema, ground facts, and a query plan to execute.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Plan {
|
||||||
|
/// Relation name → arity (column count).
|
||||||
|
pub schema: HashMap<String, usize>,
|
||||||
|
/// Relation name → list of ground tuples to insert before execution.
|
||||||
|
pub facts: HashMap<String, Vec<Vec<JsonValue>>>,
|
||||||
|
/// The join DAG itself.
|
||||||
|
pub query: Query,
|
||||||
|
/// Optional oracle: if present, [`verify`] cross-checks an executed
|
||||||
|
/// [`Relation`] against this projection. The exporter lifts the
|
||||||
|
/// scenario's `expected_bindings` block into this field.
|
||||||
|
#[serde(default)]
|
||||||
|
pub expected_bindings: Option<ExpectedBindings>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expected query result, projected to a named subset of variables. The
|
||||||
|
/// columns named here must all appear in the runner's output; any extra
|
||||||
|
/// columns (typically per-atom wildcards) are ignored.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ExpectedBindings {
|
||||||
|
pub columns: Vec<String>,
|
||||||
|
pub rows: Vec<Vec<JsonValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mirrors `Geolog.DB.Plan.PlanGraph`: a set of nodes plus the id of the
|
||||||
|
/// rooted result node (the last node in topological order).
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Query {
|
||||||
|
pub root: u32,
|
||||||
|
pub nodes: Vec<Node>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One node of the plan DAG. `id`s don't need to start at any particular
|
||||||
|
/// value, mirroring the Haskell `PlanNodeId`.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Node {
|
||||||
|
pub id: u32,
|
||||||
|
pub action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What to compute at a node. Tagged externally so JSON reads as
|
||||||
|
/// `{"action": {"scan": {...}}}` or `{"action": {"join": {...}}}`.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Action {
|
||||||
|
Scan(Atom),
|
||||||
|
Join(Join),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A flat atom pattern, one entry per column of the target relation.
|
||||||
|
/// Matches the `toFlatArgs` view used by `Geolog.DB.InMemory.evalAtom`:
|
||||||
|
/// `qaValues` positions are filled in directly, and the entity-id column
|
||||||
|
/// (if any) appears at the last position. Wildcard positions in the
|
||||||
|
/// Haskell `QAtom` (a `Map Int QVal` with a missing key) translate to a
|
||||||
|
/// fresh, unique variable name on this side, which the operator binds but
|
||||||
|
/// never joins against.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Atom {
|
||||||
|
pub table: String,
|
||||||
|
pub columns: Vec<JsonTerm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum JsonTerm {
|
||||||
|
Var(String),
|
||||||
|
Lit(JsonValue),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wire-level value tag. Restricted to what
|
||||||
|
/// [`storage::value::Value`](storage::value::Value) carries. Entity identities from
|
||||||
|
/// the Haskell side (`ValEntity path id`) round-trip through `Str` using a
|
||||||
|
/// `"path:id"` convention; that's a fixture concern, not a runner concern.
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum JsonValue {
|
||||||
|
Int(i64),
|
||||||
|
Str(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Join {
|
||||||
|
pub op: JoinOp,
|
||||||
|
pub left: u32,
|
||||||
|
pub right: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum JoinOp {
|
||||||
|
/// `Geolog.DB.Plan.LeftJoin`: result is `left` rows whose shared columns
|
||||||
|
/// appear in `right`. Lowered to `semijoin(left, right)`.
|
||||||
|
Left,
|
||||||
|
/// `Geolog.DB.Plan.RightJoin`: result is `right` rows whose shared
|
||||||
|
/// columns appear in `left`. Lowered to `semijoin(right, left)`.
|
||||||
|
Right,
|
||||||
|
/// `Geolog.DB.Plan.NaturalJoin`. Lowered to `natural_join(left, right)`.
|
||||||
|
Natural,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors produced by [`verify`] when actual bindings don't match the
|
||||||
|
/// scenario's `expected_bindings` projection.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VerifyError {
|
||||||
|
/// An expected column wasn't produced by the plan.
|
||||||
|
MissingColumn(String),
|
||||||
|
/// An expected row's width didn't match the column count.
|
||||||
|
ExpectedRowArity { expected: usize, got: usize },
|
||||||
|
/// The expected and actual rows (after projection) differ as multisets.
|
||||||
|
BindingsMismatch {
|
||||||
|
expected: Vec<Vec<Value>>,
|
||||||
|
actual: Vec<Vec<Value>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for VerifyError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::MissingColumn(name) => {
|
||||||
|
write!(f, "expected column {name:?} not in plan output")
|
||||||
|
}
|
||||||
|
Self::ExpectedRowArity { expected, got } => write!(
|
||||||
|
f,
|
||||||
|
"expected row has {got} cells but columns has {expected} entries"
|
||||||
|
),
|
||||||
|
Self::BindingsMismatch { expected, actual } => write!(
|
||||||
|
f,
|
||||||
|
"bindings mismatch:\n expected: {expected:?}\n actual: {actual:?}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for VerifyError {}
|
||||||
|
|
||||||
|
/// Cross-check an executed [`Relation`] against a [`Plan`]'s
|
||||||
|
/// `expected_bindings`. Projects `actual` to the expected columns (so the
|
||||||
|
/// runner is free to surface wildcard columns the oracle doesn't name) and
|
||||||
|
/// compares as a multiset.
|
||||||
|
///
|
||||||
|
/// Returns `Ok(true)` if the plan carried an oracle and it matched,
|
||||||
|
/// `Ok(false)` if there was no oracle (caller decides whether that's an
|
||||||
|
/// error). Returns [`VerifyError`] on mismatch.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// See [`VerifyError`].
|
||||||
|
pub fn verify(plan: &Plan, actual: &Relation) -> Result<bool, VerifyError> {
|
||||||
|
let Some(expected) = &plan.expected_bindings else {
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
let mut projection: Vec<usize> = Vec::with_capacity(expected.columns.len());
|
||||||
|
for col in &expected.columns {
|
||||||
|
let idx = actual
|
||||||
|
.columns
|
||||||
|
.iter()
|
||||||
|
.position(|c| c == col)
|
||||||
|
.ok_or_else(|| VerifyError::MissingColumn(col.clone()))?;
|
||||||
|
projection.push(idx);
|
||||||
|
}
|
||||||
|
let mut actual_proj: Vec<Vec<Value>> = actual
|
||||||
|
.rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| projection.iter().map(|&i| row[i].clone()).collect())
|
||||||
|
.collect();
|
||||||
|
let mut expected_proj: Vec<Vec<Value>> = Vec::with_capacity(expected.rows.len());
|
||||||
|
for row in &expected.rows {
|
||||||
|
if row.len() != expected.columns.len() {
|
||||||
|
return Err(VerifyError::ExpectedRowArity {
|
||||||
|
expected: expected.columns.len(),
|
||||||
|
got: row.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expected_proj.push(row.iter().cloned().map(Value::from).collect());
|
||||||
|
}
|
||||||
|
// Value is not Ord; use Debug-derived sort keys to compare as a multiset.
|
||||||
|
let key = |row: &[Value]| -> String { format!("{row:?}") };
|
||||||
|
actual_proj.sort_by_key(|r| key(r));
|
||||||
|
expected_proj.sort_by_key(|r| key(r));
|
||||||
|
if actual_proj == expected_proj {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Err(VerifyError::BindingsMismatch {
|
||||||
|
expected: expected_proj,
|
||||||
|
actual: actual_proj,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors a runner can produce during plan validation and execution.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RunError {
|
||||||
|
/// A fact or scan references a relation that isn't declared in `schema`.
|
||||||
|
UnknownRelation(String),
|
||||||
|
/// A scan refers to a table that wasn't supplied in the input map.
|
||||||
|
MissingTable(String),
|
||||||
|
/// A fact row's length doesn't match the schema's declared arity.
|
||||||
|
ArityMismatch {
|
||||||
|
relation: String,
|
||||||
|
expected: usize,
|
||||||
|
got: usize,
|
||||||
|
},
|
||||||
|
/// A scan's atom pattern doesn't match the table's arity.
|
||||||
|
PatternArityMismatch {
|
||||||
|
table: String,
|
||||||
|
table_arity: usize,
|
||||||
|
pattern_arity: usize,
|
||||||
|
},
|
||||||
|
/// A join node references a node id that doesn't exist.
|
||||||
|
MissingNode(u32),
|
||||||
|
/// `Query.root` doesn't match any node in `nodes`.
|
||||||
|
MissingRoot(u32),
|
||||||
|
/// Two nodes share the same id.
|
||||||
|
DuplicateNode(u32),
|
||||||
|
/// A join node references its left or right side before that side has
|
||||||
|
/// been computed: the DAG isn't actually topologically sorted by id, or
|
||||||
|
/// it has a cycle.
|
||||||
|
UnresolvedDependency { node: u32, depends_on: u32 },
|
||||||
|
/// A [`Storage`] backend used to materialize tables returned an error.
|
||||||
|
Storage(StorageError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StorageError> for RunError {
|
||||||
|
fn from(err: StorageError) -> Self {
|
||||||
|
Self::Storage(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RunError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::UnknownRelation(name) => {
|
||||||
|
write!(f, "facts reference relation {name:?} not in schema")
|
||||||
|
}
|
||||||
|
Self::MissingTable(name) => write!(f, "scan references missing table {name:?}"),
|
||||||
|
Self::ArityMismatch {
|
||||||
|
relation,
|
||||||
|
expected,
|
||||||
|
got,
|
||||||
|
} => write!(
|
||||||
|
f,
|
||||||
|
"relation {relation:?}: row arity {got} differs from schema arity {expected}"
|
||||||
|
),
|
||||||
|
Self::PatternArityMismatch {
|
||||||
|
table,
|
||||||
|
table_arity,
|
||||||
|
pattern_arity,
|
||||||
|
} => write!(
|
||||||
|
f,
|
||||||
|
"scan of {table:?}: pattern has {pattern_arity} columns, table has {table_arity}"
|
||||||
|
),
|
||||||
|
Self::MissingNode(id) => write!(f, "plan references missing node id {id}"),
|
||||||
|
Self::MissingRoot(id) => write!(f, "plan root id {id} matches no node"),
|
||||||
|
Self::DuplicateNode(id) => write!(f, "duplicate node id {id} in plan"),
|
||||||
|
Self::UnresolvedDependency { node, depends_on } => write!(
|
||||||
|
f,
|
||||||
|
"node {node} depends on {depends_on}, which has not been computed yet"
|
||||||
|
),
|
||||||
|
Self::Storage(err) => write!(f, "storage backend error: {err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RunError {}
|
||||||
|
|
||||||
|
impl From<JsonValue> for Value {
|
||||||
|
fn from(jv: JsonValue) -> Self {
|
||||||
|
match jv {
|
||||||
|
JsonValue::Int(n) => Self::Int(n),
|
||||||
|
JsonValue::Str(s) => Self::Str(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<JsonTerm> for Term {
|
||||||
|
fn from(t: JsonTerm) -> Self {
|
||||||
|
match t {
|
||||||
|
JsonTerm::Var(name) => Self::Var(name),
|
||||||
|
JsonTerm::Lit(value) => Self::Lit(value.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a [`Plan`] from a JSON string.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns a [`serde_json::Error`] if the input isn't valid JSON in the
|
||||||
|
/// expected shape.
|
||||||
|
pub fn parse_plan(json: &str) -> Result<Plan, serde_json::Error> {
|
||||||
|
serde_json::from_str(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the input [`Table`] for each relation declared in a [`Plan`]'s
|
||||||
|
/// schema, populating rows from the plan's `facts` map. Relations with no
|
||||||
|
/// facts get an empty table at the declared arity.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns [`RunError::UnknownRelation`] if `facts` mentions a relation
|
||||||
|
/// not in `schema`, or [`RunError::ArityMismatch`] if a row's width doesn't
|
||||||
|
/// match the declared arity.
|
||||||
|
pub fn build_tables(plan: &Plan) -> Result<HashMap<String, Table>, RunError> {
|
||||||
|
let mut tables: HashMap<String, Table> = plan
|
||||||
|
.schema
|
||||||
|
.iter()
|
||||||
|
.map(|(name, arity)| (name.clone(), Table::new(*arity)))
|
||||||
|
.collect();
|
||||||
|
for (name, rows) in &plan.facts {
|
||||||
|
let Some(table) = tables.get_mut(name) else {
|
||||||
|
return Err(RunError::UnknownRelation(name.clone()));
|
||||||
|
};
|
||||||
|
for row in rows {
|
||||||
|
if row.len() != table.arity {
|
||||||
|
return Err(RunError::ArityMismatch {
|
||||||
|
relation: name.clone(),
|
||||||
|
expected: table.arity,
|
||||||
|
got: row.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let cells: Vec<Value> = row.iter().cloned().map(Value::from).collect();
|
||||||
|
table.push(cells);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(tables)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populate a [`Storage`] backend from a [`Plan`]'s schema and facts, then
|
||||||
|
/// materialize each declared relation back into an in-memory [`Table`] via
|
||||||
|
/// [`scan_as_table`]. The returned map is the same shape [`execute`]
|
||||||
|
/// consumes, so this is the storage-backed analogue of [`build_tables`].
|
||||||
|
///
|
||||||
|
/// Adding a new backend means constructing a different `S` at the call
|
||||||
|
/// site; the body here doesn't need to change.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns [`RunError::UnknownRelation`] or [`RunError::ArityMismatch`] on
|
||||||
|
/// the same conditions as [`build_tables`], or [`RunError::Storage`] when
|
||||||
|
/// the backend itself rejects an operation.
|
||||||
|
pub fn build_tables_via_storage<S: Storage>(
|
||||||
|
plan: &Plan,
|
||||||
|
storage: &mut S,
|
||||||
|
) -> Result<HashMap<String, Table>, RunError> {
|
||||||
|
for (name, arity) in &plan.schema {
|
||||||
|
storage.create_relation(name, *arity)?;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut tx = storage.transaction()?;
|
||||||
|
for (name, rows) in &plan.facts {
|
||||||
|
let Some(&arity) = plan.schema.get(name) else {
|
||||||
|
return Err(RunError::UnknownRelation(name.clone()));
|
||||||
|
};
|
||||||
|
for row in rows {
|
||||||
|
if row.len() != arity {
|
||||||
|
return Err(RunError::ArityMismatch {
|
||||||
|
relation: name.clone(),
|
||||||
|
expected: arity,
|
||||||
|
got: row.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let cells: Vec<Value> = row.iter().cloned().map(Value::from).collect();
|
||||||
|
tx.insert(name, cells)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.commit()?;
|
||||||
|
}
|
||||||
|
let mut tables: HashMap<String, Table> = HashMap::with_capacity(plan.schema.len());
|
||||||
|
for name in plan.schema.keys() {
|
||||||
|
let table = scan_as_table(storage as &dyn Storage, name)?;
|
||||||
|
tables.insert(name.clone(), table);
|
||||||
|
}
|
||||||
|
Ok(tables)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a query DAG against the supplied input tables, returning the
|
||||||
|
/// bindings [`Relation`] for the rooted plan node.
|
||||||
|
///
|
||||||
|
/// Nodes are executed in ascending `id` order. For a Yannakakis plan as
|
||||||
|
/// emitted by `Geolog.DB.Plan` this is equivalent to a topological sort,
|
||||||
|
/// since `insertJoin` only references node ids that have already been
|
||||||
|
/// allocated. A non-monotone id ordering is rejected with
|
||||||
|
/// [`RunError::UnresolvedDependency`].
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns [`RunError::DuplicateNode`] for repeated ids,
|
||||||
|
/// [`RunError::MissingNode`] for join references to unknown ids,
|
||||||
|
/// [`RunError::MissingRoot`] if `query.root` isn't present,
|
||||||
|
/// [`RunError::MissingTable`] if a scan references a table not in the map,
|
||||||
|
/// or [`RunError::PatternArityMismatch`] if a scan's pattern doesn't match
|
||||||
|
/// the table's arity.
|
||||||
|
pub fn execute<S: std::hash::BuildHasher>(
|
||||||
|
tables: &HashMap<String, Table, S>,
|
||||||
|
query: &Query,
|
||||||
|
) -> Result<Relation, RunError> {
|
||||||
|
let mut seen_ids: std::collections::HashSet<u32> =
|
||||||
|
std::collections::HashSet::with_capacity(query.nodes.len());
|
||||||
|
for node in &query.nodes {
|
||||||
|
if !seen_ids.insert(node.id) {
|
||||||
|
return Err(RunError::DuplicateNode(node.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !seen_ids.contains(&query.root) {
|
||||||
|
return Err(RunError::MissingRoot(query.root));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ordered: Vec<&Node> = query.nodes.iter().collect();
|
||||||
|
ordered.sort_by_key(|n| n.id);
|
||||||
|
|
||||||
|
let mut results: HashMap<u32, Relation> = HashMap::with_capacity(ordered.len());
|
||||||
|
for node in ordered {
|
||||||
|
let computed = match &node.action {
|
||||||
|
Action::Scan(atom) => {
|
||||||
|
let table = tables
|
||||||
|
.get(&atom.table)
|
||||||
|
.ok_or_else(|| RunError::MissingTable(atom.table.clone()))?;
|
||||||
|
if atom.columns.len() != table.arity {
|
||||||
|
return Err(RunError::PatternArityMismatch {
|
||||||
|
table: atom.table.clone(),
|
||||||
|
table_arity: table.arity,
|
||||||
|
pattern_arity: atom.columns.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let pattern = AtomPattern {
|
||||||
|
columns: atom.columns.iter().cloned().map(Term::from).collect(),
|
||||||
|
};
|
||||||
|
scan_atom(table, &pattern)
|
||||||
|
}
|
||||||
|
Action::Join(join) => {
|
||||||
|
let left = require_dep(&results, &seen_ids, node.id, join.left)?;
|
||||||
|
let right = require_dep(&results, &seen_ids, node.id, join.right)?;
|
||||||
|
match join.op {
|
||||||
|
JoinOp::Left => semijoin(left, right),
|
||||||
|
JoinOp::Right => semijoin(right, left),
|
||||||
|
JoinOp::Natural => natural_join(left, right),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
results.insert(node.id, computed);
|
||||||
|
}
|
||||||
|
|
||||||
|
results
|
||||||
|
.remove(&query.root)
|
||||||
|
.ok_or(RunError::MissingRoot(query.root))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn require_dep<'a>(
|
||||||
|
results: &'a HashMap<u32, Relation>,
|
||||||
|
seen: &std::collections::HashSet<u32>,
|
||||||
|
node: u32,
|
||||||
|
depends_on: u32,
|
||||||
|
) -> Result<&'a Relation, RunError> {
|
||||||
|
if let Some(rel) = results.get(&depends_on) {
|
||||||
|
Ok(rel)
|
||||||
|
} else if seen.contains(&depends_on) {
|
||||||
|
Err(RunError::UnresolvedDependency { node, depends_on })
|
||||||
|
} else {
|
||||||
|
Err(RunError::MissingNode(depends_on))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience: parse JSON, build tables from the embedded facts, and
|
||||||
|
/// execute, returning the root binding relation. Equivalent to
|
||||||
|
/// `parse_plan` + [`build_tables`] + [`execute`].
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns a JSON parse error if the input is malformed, or a [`RunError`]
|
||||||
|
/// for any later step.
|
||||||
|
pub fn run_json(json: &str) -> Result<Relation, RunFromJsonError> {
|
||||||
|
let plan = parse_plan(json).map_err(RunFromJsonError::Parse)?;
|
||||||
|
let tables = build_tables(&plan).map_err(RunFromJsonError::Run)?;
|
||||||
|
let bindings = execute(&tables, &plan.query).map_err(RunFromJsonError::Run)?;
|
||||||
|
Ok(bindings)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Combined error from [`run_json`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RunFromJsonError {
|
||||||
|
Parse(serde_json::Error),
|
||||||
|
Run(RunError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for RunFromJsonError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Parse(err) => write!(f, "parse error: {err}"),
|
||||||
|
Self::Run(err) => write!(f, "run error: {err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RunFromJsonError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Self::Parse(err) => Some(err),
|
||||||
|
Self::Run(err) => Some(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
225
crates/plan-runner/src/main.rs
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
//! `plan-run` CLI: read a JSON plan from a file (or stdin if `-`), execute
|
||||||
|
//! it against the chosen backend, and print the resulting binding relation
|
||||||
|
//! as JSON on stdout.
|
||||||
|
//!
|
||||||
|
//! Backends:
|
||||||
|
//!
|
||||||
|
//! - `memory` (default): build tables straight from the plan's `facts`
|
||||||
|
//! block, no `Storage` trait involved. Pure in-memory path.
|
||||||
|
//! - `memory-storage`: load the same facts through `storage::MemoryStorage`
|
||||||
|
//! via the `Storage` trait, then materialize tables back out with
|
||||||
|
//! `scan_as_table` before executing.
|
||||||
|
//! - `lmdb`, `redb`, `fjall`, `sqlite`: file-backed `Storage` adapters.
|
||||||
|
//! Each invocation creates a fresh tempdir for the store and drops it on
|
||||||
|
//! exit; the runner is one-shot, so persistent paths aren't needed.
|
||||||
|
//! - `geomerge`: CRDT-backed adapter. Constructed in-memory; alpha-status
|
||||||
|
//! upstream.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::{self, Read};
|
||||||
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
use plan_runner::{build_tables, build_tables_via_storage, execute, parse_plan, JsonValue, Plan};
|
||||||
|
use storage::adapters::fjall::FjallStorage;
|
||||||
|
use storage::adapters::geomerge::{ColumnKind, GeomergeStorage};
|
||||||
|
use storage::adapters::lmdb::LmdbStorage;
|
||||||
|
use storage::adapters::redb::RedbStorage;
|
||||||
|
use storage::adapters::sqlite::SqliteStorage;
|
||||||
|
use storage::table::Table;
|
||||||
|
use storage::value::Value;
|
||||||
|
use storage::MemoryStorage;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum Backend {
|
||||||
|
Memory,
|
||||||
|
MemoryStorage,
|
||||||
|
Lmdb,
|
||||||
|
Redb,
|
||||||
|
Fjall,
|
||||||
|
Sqlite,
|
||||||
|
Geomerge,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Backend {
|
||||||
|
fn parse(s: &str) -> Option<Self> {
|
||||||
|
match s {
|
||||||
|
"memory" => Some(Self::Memory),
|
||||||
|
"memory-storage" => Some(Self::MemoryStorage),
|
||||||
|
"lmdb" => Some(Self::Lmdb),
|
||||||
|
"redb" => Some(Self::Redb),
|
||||||
|
"fjall" => Some(Self::Fjall),
|
||||||
|
"sqlite" => Some(Self::Sqlite),
|
||||||
|
"geomerge" => Some(Self::Geomerge),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const BACKENDS_HELP: &str = "memory|memory-storage|lmdb|redb|fjall|sqlite|geomerge";
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
let mut backend = Backend::Memory;
|
||||||
|
let mut input_path: Option<String> = None;
|
||||||
|
let mut args = std::env::args().skip(1);
|
||||||
|
while let Some(arg) = args.next() {
|
||||||
|
match arg.as_str() {
|
||||||
|
"--backend" => {
|
||||||
|
let Some(value) = args.next() else {
|
||||||
|
eprintln!("--backend requires a value ({BACKENDS_HELP})");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
};
|
||||||
|
let Some(parsed) = Backend::parse(&value) else {
|
||||||
|
eprintln!("unknown backend {value:?} (try {BACKENDS_HELP})");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
};
|
||||||
|
backend = parsed;
|
||||||
|
}
|
||||||
|
other if input_path.is_none() => input_path = Some(other.to_string()),
|
||||||
|
other => {
|
||||||
|
eprintln!("unexpected argument: {other}");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let Some(path) = input_path else {
|
||||||
|
eprintln!("usage: plan-run [--backend {BACKENDS_HELP}] <plan.json | ->");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
let input = match read_input(&path) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("failed to read {path}: {err}");
|
||||||
|
return ExitCode::from(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let plan = match parse_plan(&input) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("parse error: {err}");
|
||||||
|
return ExitCode::from(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let tables = match build_tables_for(&plan, backend) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{err}");
|
||||||
|
return ExitCode::from(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let relation = match execute(&tables, &plan.query) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("execute error: {err}");
|
||||||
|
return ExitCode::from(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload = serde_json::json!({
|
||||||
|
"columns": relation.columns,
|
||||||
|
"rows": relation
|
||||||
|
.rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| row.iter().map(value_to_json).collect::<Vec<_>>())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
});
|
||||||
|
println!("{payload}");
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the input tables for `plan` using `backend`. Path-based adapters
|
||||||
|
/// allocate a fresh tempdir; it drops at the end of this function, which is
|
||||||
|
/// safe because `build_tables_via_storage` fully materializes the tables
|
||||||
|
/// into owned `Vec<Value>` before returning.
|
||||||
|
fn build_tables_for(plan: &Plan, backend: Backend) -> Result<HashMap<String, Table>, String> {
|
||||||
|
match backend {
|
||||||
|
Backend::Memory => build_tables(plan).map_err(|e| format!("build error: {e}")),
|
||||||
|
Backend::MemoryStorage => {
|
||||||
|
let mut storage = MemoryStorage::default();
|
||||||
|
build_tables_via_storage(plan, &mut storage)
|
||||||
|
.map_err(|e| format!("build error (memory-storage): {e}"))
|
||||||
|
}
|
||||||
|
Backend::Lmdb => {
|
||||||
|
let dir = TempDir::new().map_err(|e| format!("tempdir: {e}"))?;
|
||||||
|
let mut storage = LmdbStorage::open(dir.path())
|
||||||
|
.map_err(|e| format!("failed to open lmdb backend: {e}"))?;
|
||||||
|
build_tables_via_storage(plan, &mut storage)
|
||||||
|
.map_err(|e| format!("build error (lmdb): {e}"))
|
||||||
|
}
|
||||||
|
Backend::Redb => {
|
||||||
|
let dir = TempDir::new().map_err(|e| format!("tempdir: {e}"))?;
|
||||||
|
let mut storage = RedbStorage::open(dir.path().join("data.redb"))
|
||||||
|
.map_err(|e| format!("failed to open redb backend: {e}"))?;
|
||||||
|
build_tables_via_storage(plan, &mut storage)
|
||||||
|
.map_err(|e| format!("build error (redb): {e}"))
|
||||||
|
}
|
||||||
|
Backend::Fjall => {
|
||||||
|
let dir = TempDir::new().map_err(|e| format!("tempdir: {e}"))?;
|
||||||
|
let mut storage = FjallStorage::open(dir.path())
|
||||||
|
.map_err(|e| format!("failed to open fjall backend: {e}"))?;
|
||||||
|
build_tables_via_storage(plan, &mut storage)
|
||||||
|
.map_err(|e| format!("build error (fjall): {e}"))
|
||||||
|
}
|
||||||
|
Backend::Sqlite => {
|
||||||
|
let dir = TempDir::new().map_err(|e| format!("tempdir: {e}"))?;
|
||||||
|
let mut storage = SqliteStorage::open(dir.path().join("data.sqlite"))
|
||||||
|
.map_err(|e| format!("failed to open sqlite backend: {e}"))?;
|
||||||
|
build_tables_via_storage(plan, &mut storage)
|
||||||
|
.map_err(|e| format!("build error (sqlite): {e}"))
|
||||||
|
}
|
||||||
|
Backend::Geomerge => {
|
||||||
|
let relations = plan
|
||||||
|
.schema
|
||||||
|
.iter()
|
||||||
|
.map(|(name, &arity)| (name.clone(), infer_column_kinds(plan, name, arity)));
|
||||||
|
let mut storage = GeomergeStorage::with_relations(relations)
|
||||||
|
.map_err(|e| format!("failed to open geomerge backend: {e}"))?;
|
||||||
|
build_tables_via_storage(plan, &mut storage)
|
||||||
|
.map_err(|e| format!("build error (geomerge): {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Best-effort column type inference for `geomerge`'s synthesized theory.
|
||||||
|
/// The runner IR carries only arity, so we peek at the first fact row of
|
||||||
|
/// the relation. Columns without a sample default to `String`, which
|
||||||
|
/// matches every checked-in fixture (entity identities are encoded as
|
||||||
|
/// strings by the exporter).
|
||||||
|
fn infer_column_kinds(plan: &Plan, name: &str, arity: usize) -> Vec<ColumnKind> {
|
||||||
|
let mut kinds = vec![ColumnKind::String; arity];
|
||||||
|
let Some(rows) = plan.facts.get(name) else {
|
||||||
|
return kinds;
|
||||||
|
};
|
||||||
|
let Some(first) = rows.first() else {
|
||||||
|
return kinds;
|
||||||
|
};
|
||||||
|
for (i, cell) in first.iter().take(arity).enumerate() {
|
||||||
|
kinds[i] = match cell {
|
||||||
|
JsonValue::Int(_) => ColumnKind::Int,
|
||||||
|
JsonValue::Str(_) => ColumnKind::String,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
kinds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_input(path: &str) -> io::Result<String> {
|
||||||
|
if path == "-" {
|
||||||
|
let mut buf = String::new();
|
||||||
|
io::stdin().read_to_string(&mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
} else {
|
||||||
|
std::fs::read_to_string(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_to_json(value: &Value) -> serde_json::Value {
|
||||||
|
match value {
|
||||||
|
Value::Int(n) => serde_json::Value::Number((*n).into()),
|
||||||
|
Value::Str(s) => serde_json::Value::String(s.clone()),
|
||||||
|
Value::Id(id) => serde_json::Value::String(id.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
77
crates/plan-runner/tests/examples.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//! Walks every JSON fixture under `crates/plan-runner/fixtures/` and
|
||||||
|
//! verifies it against the `expected_bindings` the exporter lifted from
|
||||||
|
//! the matching `tools/exporter/examples/*.scenario.json`. A fixture without an oracle
|
||||||
|
//! is reported as a failure (every checked-in fixture is expected to
|
||||||
|
//! carry one).
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use plan_runner::{parse_plan, run_json, verify};
|
||||||
|
|
||||||
|
fn fixtures_dir() -> PathBuf {
|
||||||
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_fixtures() -> BTreeMap<String, String> {
|
||||||
|
let mut out = BTreeMap::new();
|
||||||
|
for entry in fs::read_dir(fixtures_dir()).expect("read fixtures/") {
|
||||||
|
let path = entry.expect("dir entry").path();
|
||||||
|
if path.extension().and_then(|e| e.to_str()) != Some("json") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let name = path
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.expect("ascii fixture name")
|
||||||
|
.to_string();
|
||||||
|
let contents = fs::read_to_string(&path).expect("read fixture");
|
||||||
|
out.insert(name, contents);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_fixture_runs_and_matches_its_oracle() {
|
||||||
|
let fixtures = collect_fixtures();
|
||||||
|
assert!(
|
||||||
|
!fixtures.is_empty(),
|
||||||
|
"no fixtures found in {}",
|
||||||
|
fixtures_dir().display()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut failures: Vec<String> = Vec::new();
|
||||||
|
for (name, json) in &fixtures {
|
||||||
|
let plan = match parse_plan(json) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(err) => {
|
||||||
|
failures.push(format!("{name}: parse error: {err}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if plan.expected_bindings.is_none() {
|
||||||
|
failures.push(format!("{name}: fixture has no expected_bindings"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let relation = match run_json(json) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(err) => {
|
||||||
|
failures.push(format!("{name}: run error: {err}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match verify(&plan, &relation) {
|
||||||
|
Ok(true) => {}
|
||||||
|
Ok(false) => failures.push(format!("{name}: verify returned no-oracle unexpectedly")),
|
||||||
|
Err(err) => failures.push(format!("{name}: {err}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
failures.is_empty(),
|
||||||
|
"{} fixture(s) failed:\n {}",
|
||||||
|
failures.len(),
|
||||||
|
failures.join("\n ")
|
||||||
|
);
|
||||||
|
}
|
||||||
52
crates/plan-runner/tests/storage_roundtrip.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
//! Cross-checks the two paths [`plan-runner`] exposes for materializing
|
||||||
|
//! input tables: the pure [`build_tables`] path and the [`Storage`]-routed
|
||||||
|
//! [`build_tables_via_storage`] path. Same fixture, same plan, must agree
|
||||||
|
//! row-for-row.
|
||||||
|
//!
|
||||||
|
//! This is the visible proof of the layer boundary: any new `Storage`
|
||||||
|
//! backend (LMDB, fjall, geomerge) keeps this test honest by re-running it
|
||||||
|
//! with a different `S`.
|
||||||
|
|
||||||
|
use plan_runner::{build_tables, build_tables_via_storage, execute, parse_plan, run_json};
|
||||||
|
use storage::value::Value;
|
||||||
|
use storage::MemoryStorage;
|
||||||
|
|
||||||
|
const FIXTURE: &str = include_str!("../fixtures/three_atom_chain.json");
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn storage_backed_execution_matches_pure_path() {
|
||||||
|
let plan = parse_plan(FIXTURE).expect("parse plan");
|
||||||
|
|
||||||
|
let pure_tables = build_tables(&plan).expect("build_tables");
|
||||||
|
let pure = execute(&pure_tables, &plan.query).expect("pure execute");
|
||||||
|
|
||||||
|
let mut storage = MemoryStorage::default();
|
||||||
|
let storage_tables =
|
||||||
|
build_tables_via_storage(&plan, &mut storage).expect("build_tables_via_storage");
|
||||||
|
let via_storage = execute(&storage_tables, &plan.query).expect("storage execute");
|
||||||
|
|
||||||
|
assert_eq!(pure.columns, via_storage.columns);
|
||||||
|
// Scan order between MemoryStorage and the direct-from-JSON path isn't
|
||||||
|
// required to match; compare rows as a multiset. `Value` is not `Ord`
|
||||||
|
// (it carries `RowId` and `String`), so use a Debug-derived sort key.
|
||||||
|
assert_eq!(sorted_rows(&pure.rows), sorted_rows(&via_storage.rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn storage_backed_execution_matches_run_json_oracle() {
|
||||||
|
let plan = parse_plan(FIXTURE).expect("parse plan");
|
||||||
|
let oracle = run_json(FIXTURE).expect("run_json");
|
||||||
|
|
||||||
|
let mut storage = MemoryStorage::default();
|
||||||
|
let tables = build_tables_via_storage(&plan, &mut storage).expect("build_tables_via_storage");
|
||||||
|
let via_storage = execute(&tables, &plan.query).expect("storage execute");
|
||||||
|
|
||||||
|
assert_eq!(oracle.columns, via_storage.columns);
|
||||||
|
assert_eq!(sorted_rows(&oracle.rows), sorted_rows(&via_storage.rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sorted_rows(rows: &[Vec<Value>]) -> Vec<String> {
|
||||||
|
let mut keys: Vec<String> = rows.iter().map(|r| format!("{r:?}")).collect();
|
||||||
|
keys.sort();
|
||||||
|
keys
|
||||||
|
}
|
||||||
@ -121,7 +121,7 @@ How it works (logically):
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<picture>
|
<picture>
|
||||||
<img alt="Types" src="docs/diagrams/workflow.svg" height="90%" width="90%%">
|
<img alt="Workflow" src="docs/diagrams/workflow.svg" height="90%" width="90%">
|
||||||
</picture>
|
</picture>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ penwidth = 1.2
|
|||||||
]
|
]
|
||||||
|
|
||||||
table_node [label = <<table border="0" cellborder="0" cellspacing="0" cellpadding="4">
|
table_node [label = <<table border="0" cellborder="0" cellspacing="0" cellpadding="4">
|
||||||
<tr><td align="center"><b>Table</b> (struct)</td></tr>
|
<tr><td align="center"><b>Table</b> (struct, from storage)</td></tr>
|
||||||
<tr><td align="left" balign="left">arity: usize</td></tr>
|
<tr><td align="left" balign="left">arity: usize</td></tr>
|
||||||
<tr><td align="left" balign="left">rows: Vec<Vec<Value>></td></tr>
|
<tr><td align="left" balign="left">rows: Vec<Vec<Value>></td></tr>
|
||||||
</table>>, fillcolor = "#E8F4FD", color = "#2196F3"]
|
</table>>, fillcolor = "#E8F4FD", color = "#2196F3"]
|
||||||
@ -47,7 +47,7 @@ term_node [label = <<table border="0" cellborder="0" cellspacing="0" cellpadding
|
|||||||
</table>>, fillcolor = "#F3E5F5", color = "#9C27B0"]
|
</table>>, fillcolor = "#F3E5F5", color = "#9C27B0"]
|
||||||
|
|
||||||
value_node [label = <<table border="0" cellborder="0" cellspacing="0" cellpadding="4">
|
value_node [label = <<table border="0" cellborder="0" cellspacing="0" cellpadding="4">
|
||||||
<tr><td align="center"><b>Value</b> (enum)</td></tr>
|
<tr><td align="center"><b>Value</b> (enum, from storage)</td></tr>
|
||||||
<tr><td align="left" balign="left">Int(i64)</td></tr>
|
<tr><td align="left" balign="left">Int(i64)</td></tr>
|
||||||
<tr><td align="left" balign="left">Str(String)</td></tr>
|
<tr><td align="left" balign="left">Str(String)</td></tr>
|
||||||
<tr><td align="left" balign="left">Id(RowId)</td></tr>
|
<tr><td align="left" balign="left">Id(RowId)</td></tr>
|
||||||
|
|||||||
@ -4,144 +4,83 @@
|
|||||||
<!-- Generated by graphviz version 12.2.1 (0)
|
<!-- Generated by graphviz version 12.2.1 (0)
|
||||||
-->
|
-->
|
||||||
<!-- Title: QueryOpsTypes Pages: 1 -->
|
<!-- Title: QueryOpsTypes Pages: 1 -->
|
||||||
<svg width="584pt" height="420pt"
|
<svg width="604pt" height="420pt"
|
||||||
viewBox="0.00 0.00 583.50 420.00" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0.00 0.00 603.62 420.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 416)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 416)">
|
||||||
<title>QueryOpsTypes</title>
|
<title>QueryOpsTypes</title>
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-416 579.5,-416 579.5,4 -4,4"/>
|
<polygon fill="white" stroke="none" points="-4,4 -4,-416 599.62,-416 599.62,4 -4,4"/>
|
||||||
<!-- table_node -->
|
<!-- table_node -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
<title>table_node</title>
|
<title>table_node</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M180,-282.5C180,-282.5 12,-282.5 12,-282.5 6,-282.5 0,-276.5 0,-270.5 0,-270.5 0,-199.5 0,-199.5 0,-193.5 6,-187.5 12,-187.5 12,-187.5 180,-187.5 180,-187.5 186,-187.5 192,-193.5 192,-199.5 192,-199.5 192,-270.5 192,-270.5 192,-276.5 186,-282.5 180,-282.5"/>
|
||||||
d="M159.75,-282.5C159.75,-282.5 12,-282.5 12,-282.5 6,-282.5 0,-276.5 0,-270.5 0,-270.5 0,-199.5 0,-199.5 0,-193.5 6,-187.5 12,-187.5 12,-187.5 159.75,-187.5 159.75,-187.5 165.75,-187.5 171.75,-193.5 171.75,-199.5 171.75,-199.5 171.75,-270.5 171.75,-270.5 171.75,-276.5 165.75,-282.5 159.75,-282.5"/>
|
<text text-anchor="start" x="12" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Table</text>
|
||||||
<text text-anchor="start" x="43.88" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="46.5" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (struct, from storage)</text>
|
||||||
font-size="14.00">Table
|
<text text-anchor="start" x="12" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">arity: usize</text>
|
||||||
</text>
|
<text text-anchor="start" x="12" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">rows: Vec<Vec<Value>></text>
|
||||||
<text text-anchor="start" x="78.38" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (struct)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="12" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
arity: usize
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="12" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">rows:
|
|
||||||
Vec<Vec<Value>>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- value_node -->
|
<!-- value_node -->
|
||||||
<g id="node5" class="node">
|
<g id="node5" class="node">
|
||||||
<title>value_node</title>
|
<title>value_node</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M413.5,-124C413.5,-124 242.5,-124 242.5,-124 236.5,-124 230.5,-118 230.5,-112 230.5,-112 230.5,-12 230.5,-12 230.5,-6 236.5,0 242.5,0 242.5,0 413.5,0 413.5,0 419.5,0 425.5,-6 425.5,-12 425.5,-12 425.5,-112 425.5,-112 425.5,-118 419.5,-124 413.5,-124"/>
|
||||||
d="M351.38,-124C351.38,-124 264.38,-124 264.38,-124 258.38,-124 252.38,-118 252.38,-112 252.38,-112 252.38,-12 252.38,-12 252.38,-6 258.38,0 264.38,0 264.38,0 351.38,0 351.38,0 357.38,0 363.38,-6 363.38,-12 363.38,-12 363.38,-112 363.38,-112 363.38,-118 357.38,-124 351.38,-124"/>
|
<text text-anchor="start" x="242.5" y="-103.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Value</text>
|
||||||
<text text-anchor="start" x="264.38" y="-103.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="278.5" y="-103.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (enum, from storage)</text>
|
||||||
font-size="14.00">Value
|
<text text-anchor="start" x="242.5" y="-73.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Int(i64)</text>
|
||||||
</text>
|
<text text-anchor="start" x="242.5" y="-44.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Str(String)</text>
|
||||||
<text text-anchor="start" x="300.38" y="-103.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="242.5" y="-15.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Id(RowId)</text>
|
||||||
 (enum)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="264.38" y="-73.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Int(i64)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="264.38" y="-44.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Str(String)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="264.38" y="-15.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Id(RowId)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- table_node->value_node -->
|
<!-- table_node->value_node -->
|
||||||
<g id="edge3" class="edge">
|
<g id="edge3" class="edge">
|
||||||
<title>table_node->value_node</title>
|
<title>table_node->value_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M159.7,-187.05C183.12,-169.79 210.13,-149.88 235.56,-131.13"/>
|
||||||
d="M146.83,-187.05C176.61,-164.11 212.47,-136.49 242.78,-113.14"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="237.42,-134.11 243.39,-125.36 233.27,-128.48 237.42,-134.11"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="242.17" y="-153.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Vec<Vec<Value>></text>
|
||||||
points="244.77,-116.02 250.56,-107.15 240.5,-110.48 244.77,-116.02"/>
|
|
||||||
<text text-anchor="middle" x="227.34" y="-153.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Vec<Vec<Value>>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- relation_node -->
|
<!-- relation_node -->
|
||||||
<g id="node2" class="node">
|
<g id="node2" class="node">
|
||||||
<title>relation_node</title>
|
<title>relation_node</title>
|
||||||
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5"
|
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5" d="M401.88,-282.5C401.88,-282.5 254.12,-282.5 254.12,-282.5 248.12,-282.5 242.12,-276.5 242.12,-270.5 242.12,-270.5 242.12,-199.5 242.12,-199.5 242.12,-193.5 248.12,-187.5 254.12,-187.5 254.12,-187.5 401.88,-187.5 401.88,-187.5 407.88,-187.5 413.88,-193.5 413.88,-199.5 413.88,-199.5 413.88,-270.5 413.88,-270.5 413.88,-276.5 407.88,-282.5 401.88,-282.5"/>
|
||||||
d="M381.75,-282.5C381.75,-282.5 234,-282.5 234,-282.5 228,-282.5 222,-276.5 222,-270.5 222,-270.5 222,-199.5 222,-199.5 222,-193.5 228,-187.5 234,-187.5 234,-187.5 381.75,-187.5 381.75,-187.5 387.75,-187.5 393.75,-193.5 393.75,-199.5 393.75,-199.5 393.75,-270.5 393.75,-270.5 393.75,-276.5 387.75,-282.5 381.75,-282.5"/>
|
<text text-anchor="start" x="276.62" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Relation</text>
|
||||||
<text text-anchor="start" x="256.5" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="329.88" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (struct)</text>
|
||||||
font-size="14.00">Relation
|
<text text-anchor="start" x="254.12" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">columns: Vec<String></text>
|
||||||
</text>
|
<text text-anchor="start" x="254.12" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">rows: Vec<Vec<Value>></text>
|
||||||
<text text-anchor="start" x="309.75" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (struct)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="234" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
columns: Vec<String>
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="234" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
rows: Vec<Vec<Value>>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- relation_node->value_node -->
|
<!-- relation_node->value_node -->
|
||||||
<g id="edge4" class="edge">
|
<g id="edge4" class="edge">
|
||||||
<title>relation_node->value_node</title>
|
<title>relation_node->value_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M328,-187.27C328,-171.37 328,-153.21 328,-135.76"/>
|
||||||
d="M307.88,-187.27C307.88,-171.37 307.88,-153.21 307.88,-135.76"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="331.5,-136.25 328,-126.25 324.5,-136.25 331.5,-136.25"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="365.12" y="-153.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Vec<Vec<Value>></text>
|
||||||
points="311.38,-136.25 307.88,-126.25 304.38,-136.25 311.38,-136.25"/>
|
|
||||||
<text text-anchor="middle" x="345" y="-153.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Vec<Vec<Value>>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- atom_pattern_node -->
|
<!-- atom_pattern_node -->
|
||||||
<g id="node3" class="node">
|
<g id="node3" class="node">
|
||||||
<title>atom_pattern_node</title>
|
<title>atom_pattern_node</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M583.62,-412C583.62,-412 452.38,-412 452.38,-412 446.38,-412 440.38,-406 440.38,-400 440.38,-400 440.38,-358 440.38,-358 440.38,-352 446.38,-346 452.38,-346 452.38,-346 583.62,-346 583.62,-346 589.62,-346 595.62,-352 595.62,-358 595.62,-358 595.62,-400 595.62,-400 595.62,-406 589.62,-412 583.62,-412"/>
|
||||||
d="M563.5,-412C563.5,-412 432.25,-412 432.25,-412 426.25,-412 420.25,-406 420.25,-400 420.25,-400 420.25,-358 420.25,-358 420.25,-352 426.25,-346 432.25,-346 432.25,-346 563.5,-346 563.5,-346 569.5,-346 575.5,-352 575.5,-358 575.5,-358 575.5,-400 575.5,-400 575.5,-406 569.5,-412 563.5,-412"/>
|
<text text-anchor="start" x="452.38" y="-391.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">AtomPattern</text>
|
||||||
<text text-anchor="start" x="432.25" y="-391.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="534.12" y="-391.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (struct)</text>
|
||||||
font-size="14.00">AtomPattern
|
<text text-anchor="start" x="452.38" y="-361.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">columns: Vec<Term></text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="514" y="-391.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (struct)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="432.25" y="-361.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
columns: Vec<Term>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- term_node -->
|
<!-- term_node -->
|
||||||
<g id="node4" class="node">
|
<g id="node4" class="node">
|
||||||
<title>term_node</title>
|
<title>term_node</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M560,-282.5C560,-282.5 476,-282.5 476,-282.5 470,-282.5 464,-276.5 464,-270.5 464,-270.5 464,-199.5 464,-199.5 464,-193.5 470,-187.5 476,-187.5 476,-187.5 560,-187.5 560,-187.5 566,-187.5 572,-193.5 572,-199.5 572,-199.5 572,-270.5 572,-270.5 572,-276.5 566,-282.5 560,-282.5"/>
|
||||||
d="M539.88,-282.5C539.88,-282.5 455.88,-282.5 455.88,-282.5 449.88,-282.5 443.88,-276.5 443.88,-270.5 443.88,-270.5 443.88,-199.5 443.88,-199.5 443.88,-193.5 449.88,-187.5 455.88,-187.5 455.88,-187.5 539.88,-187.5 539.88,-187.5 545.88,-187.5 551.88,-193.5 551.88,-199.5 551.88,-199.5 551.88,-270.5 551.88,-270.5 551.88,-276.5 545.88,-282.5 539.88,-282.5"/>
|
<text text-anchor="start" x="476" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Term</text>
|
||||||
<text text-anchor="start" x="455.88" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="509" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (enum)</text>
|
||||||
font-size="14.00">Term
|
<text text-anchor="start" x="476" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Var(String)</text>
|
||||||
</text>
|
<text text-anchor="start" x="476" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Lit(Value)</text>
|
||||||
<text text-anchor="start" x="488.88" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (enum)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="455.88" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Var(String)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="455.88" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Lit(Value)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- atom_pattern_node->term_node -->
|
<!-- atom_pattern_node->term_node -->
|
||||||
<g id="edge1" class="edge">
|
<g id="edge1" class="edge">
|
||||||
<title>atom_pattern_node->term_node</title>
|
<title>atom_pattern_node->term_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M518,-345.78C518,-330.61 518,-312.04 518,-294.52"/>
|
||||||
d="M497.88,-345.78C497.88,-330.61 497.88,-312.04 497.88,-294.52"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="521.5,-294.73 518,-284.73 514.5,-294.73 521.5,-294.73"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="540.88" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Vec<Term></text>
|
||||||
points="501.38,-294.73 497.88,-284.73 494.38,-294.73 501.38,-294.73"/>
|
|
||||||
<text text-anchor="middle" x="520.75" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Vec<Term>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- term_node->value_node -->
|
<!-- term_node->value_node -->
|
||||||
<g id="edge2" class="edge">
|
<g id="edge2" class="edge">
|
||||||
<title>term_node->value_node</title>
|
<title>term_node->value_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M465.83,-187.05C446.99,-170.09 425.31,-150.58 404.8,-132.12"/>
|
||||||
d="M445.71,-187.05C423.01,-166.62 396.18,-142.47 372.26,-120.94"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="407.22,-129.59 397.44,-125.5 402.53,-134.79 407.22,-129.59"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="453.88" y="-153.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Lit(Value)</text>
|
||||||
points="374.71,-118.44 364.94,-114.35 370.03,-123.65 374.71,-118.44"/>
|
|
||||||
<text text-anchor="middle" x="433.75" y="-153.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Lit(Value)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 7.3 KiB |
@ -5,253 +5,155 @@
|
|||||||
-->
|
-->
|
||||||
<!-- Title: QueryOpsHandPlan Pages: 1 -->
|
<!-- Title: QueryOpsHandPlan Pages: 1 -->
|
||||||
<svg width="1482pt" height="471pt"
|
<svg width="1482pt" height="471pt"
|
||||||
viewBox="0.00 0.00 1481.75 471.00" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0.00 0.00 1481.75 471.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 467)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 467)">
|
||||||
<title>QueryOpsHandPlan</title>
|
<title>QueryOpsHandPlan</title>
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-467 1477.75,-467 1477.75,4 -4,4"/>
|
<polygon fill="white" stroke="none" points="-4,4 -4,-467 1477.75,-467 1477.75,4 -4,4"/>
|
||||||
<g id="clust1" class="cluster">
|
<g id="clust1" class="cluster">
|
||||||
<title>cluster_inputs</title>
|
<title>cluster_inputs</title>
|
||||||
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2" points="8,-8 8,-455 198.5,-455 198.5,-8 8,-8"/>
|
||||||
points="8,-8 8,-455 198.5,-455 198.5,-8 8,-8"/>
|
<text text-anchor="middle" x="103.25" y="-437.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#555555">Inputs (positional tables)</text>
|
||||||
<text text-anchor="middle" x="103.25" y="-437.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#555555">Inputs (positional tables)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust2" class="cluster">
|
<g id="clust2" class="cluster">
|
||||||
<title>cluster_atoms</title>
|
<title>cluster_atoms</title>
|
||||||
<polygon fill="white" stroke="#9c27b0" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#9c27b0" stroke-dasharray="5,2" points="233.5,-12 233.5,-451 609.5,-451 609.5,-12 233.5,-12"/>
|
||||||
points="233.5,-12 233.5,-451 609.5,-451 609.5,-12 233.5,-12"/>
|
<text text-anchor="middle" x="421.5" y="-433.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#7b1fa2">Atom Scans  (scan_atom: Table × AtomPattern → Relation)</text>
|
||||||
<text text-anchor="middle" x="421.5" y="-433.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#7b1fa2">Atom Scans  (scan_atom: Table × AtomPattern → Relation)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust3" class="cluster">
|
<g id="clust3" class="cluster">
|
||||||
<title>cluster_joins</title>
|
<title>cluster_joins</title>
|
||||||
<polygon fill="white" stroke="#4caf50" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#4caf50" stroke-dasharray="5,2" points="665.5,-141 665.5,-322 1106,-322 1106,-141 665.5,-141"/>
|
||||||
points="665.5,-141 665.5,-322 1106,-322 1106,-141 665.5,-141"/>
|
<text text-anchor="middle" x="885.75" y="-304.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#388e3c">Joins  (shared cols = matching column names)</text>
|
||||||
<text text-anchor="middle" x="885.75" y="-304.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#388e3c">Joins  (shared cols = matching column names)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust4" class="cluster">
|
<g id="clust4" class="cluster">
|
||||||
<title>cluster_output</title>
|
<title>cluster_output</title>
|
||||||
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2" points="1141,-152 1141,-311 1465.75,-311 1465.75,-152 1141,-152"/>
|
||||||
points="1141,-152 1141,-311 1465.75,-311 1465.75,-152 1141,-152"/>
|
<text text-anchor="middle" x="1303.38" y="-293.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#555555">Output (binding relation)</text>
|
||||||
<text text-anchor="middle" x="1303.38" y="-293.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#555555">Output (binding relation)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- author_table -->
|
<!-- author_table -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
<title>author_table</title>
|
<title>author_table</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M165.88,-408.12C165.88,-408.12 40.62,-408.12 40.62,-408.12 34.62,-408.12 28.62,-402.12 28.62,-396.12 28.62,-396.12 28.62,-325.88 28.62,-325.88 28.62,-319.88 34.62,-313.88 40.62,-313.88 40.62,-313.88 165.88,-313.88 165.88,-313.88 171.88,-313.88 177.88,-319.88 177.88,-325.88 177.88,-325.88 177.88,-396.12 177.88,-396.12 177.88,-402.12 171.88,-408.12 165.88,-408.12"/>
|
||||||
d="M165.88,-408.12C165.88,-408.12 40.62,-408.12 40.62,-408.12 34.62,-408.12 28.62,-402.12 28.62,-396.12 28.62,-396.12 28.62,-325.88 28.62,-325.88 28.62,-319.88 34.62,-313.88 40.62,-313.88 40.62,-313.88 165.88,-313.88 165.88,-313.88 171.88,-313.88 177.88,-319.88 177.88,-325.88 177.88,-325.88 177.88,-396.12 177.88,-396.12 177.88,-402.12 171.88,-408.12 165.88,-408.12"/>
|
<text text-anchor="start" x="60.88" y="-387.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Table: author</text>
|
||||||
<text text-anchor="start" x="60.88" y="-387.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="40.62" y="-358.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• arity 2</text>
|
||||||
font-size="14.00">Table: author
|
<text text-anchor="start" x="40.62" y="-329.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• rows: (name, book)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="40.62" y="-358.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
arity 2
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="40.62" y="-329.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
rows: (name, book)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- author_rel -->
|
<!-- author_rel -->
|
||||||
<g id="node4" class="node">
|
<g id="node4" class="node">
|
||||||
<title>author_rel</title>
|
<title>author_rel</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M509.12,-408.12C509.12,-408.12 332.88,-408.12 332.88,-408.12 326.88,-408.12 320.88,-402.12 320.88,-396.12 320.88,-396.12 320.88,-325.88 320.88,-325.88 320.88,-319.88 326.88,-313.88 332.88,-313.88 332.88,-313.88 509.12,-313.88 509.12,-313.88 515.12,-313.88 521.12,-319.88 521.12,-325.88 521.12,-325.88 521.12,-396.12 521.12,-396.12 521.12,-402.12 515.12,-408.12 509.12,-408.12"/>
|
||||||
d="M509.12,-408.12C509.12,-408.12 332.88,-408.12 332.88,-408.12 326.88,-408.12 320.88,-402.12 320.88,-396.12 320.88,-396.12 320.88,-325.88 320.88,-325.88 320.88,-319.88 326.88,-313.88 332.88,-313.88 332.88,-313.88 509.12,-313.88 509.12,-313.88 515.12,-313.88 521.12,-319.88 521.12,-325.88 521.12,-325.88 521.12,-396.12 521.12,-396.12 521.12,-402.12 515.12,-408.12 509.12,-408.12"/>
|
<text text-anchor="start" x="388" y="-387.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">author_rel</text>
|
||||||
<text text-anchor="start" x="388" y="-387.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="332.88" y="-358.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">pattern: [Var name, Var book]</text>
|
||||||
font-size="14.00">author_rel
|
<text text-anchor="start" x="332.88" y="-329.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">cols: [name, book]</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="332.88" y="-358.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
pattern: [Var name, Var book]
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="332.88" y="-329.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
cols: [name, book]
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- author_table->author_rel -->
|
<!-- author_table->author_rel -->
|
||||||
<g id="edge1" class="edge">
|
<g id="edge1" class="edge">
|
||||||
<title>author_table->author_rel</title>
|
<title>author_table->author_rel</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M178.28,-361C217.1,-361 265.45,-361 308.68,-361"/>
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M178.28,-361C217.1,-361 265.45,-361 308.68,-361"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="308.62,-364.5 318.62,-361 308.62,-357.5 308.62,-364.5"/>
|
||||||
points="308.62,-364.5 318.62,-361 308.62,-357.5 308.62,-364.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- bestseller_table -->
|
<!-- bestseller_table -->
|
||||||
<g id="node2" class="node">
|
<g id="node2" class="node">
|
||||||
<title>bestseller_table</title>
|
<title>bestseller_table</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M156.12,-264.12C156.12,-264.12 50.38,-264.12 50.38,-264.12 44.38,-264.12 38.38,-258.12 38.38,-252.12 38.38,-252.12 38.38,-181.88 38.38,-181.88 38.38,-175.88 44.38,-169.88 50.38,-169.88 50.38,-169.88 156.12,-169.88 156.12,-169.88 162.12,-169.88 168.12,-175.88 168.12,-181.88 168.12,-181.88 168.12,-252.12 168.12,-252.12 168.12,-258.12 162.12,-264.12 156.12,-264.12"/>
|
||||||
d="M156.12,-264.12C156.12,-264.12 50.38,-264.12 50.38,-264.12 44.38,-264.12 38.38,-258.12 38.38,-252.12 38.38,-252.12 38.38,-181.88 38.38,-181.88 38.38,-175.88 44.38,-169.88 50.38,-169.88 50.38,-169.88 156.12,-169.88 156.12,-169.88 162.12,-169.88 168.12,-175.88 168.12,-181.88 168.12,-181.88 168.12,-252.12 168.12,-252.12 168.12,-258.12 162.12,-264.12 156.12,-264.12"/>
|
<text text-anchor="start" x="50.38" y="-243.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Table: bestseller</text>
|
||||||
<text text-anchor="start" x="50.38" y="-243.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="50.38" y="-214.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• arity 1</text>
|
||||||
font-size="14.00">Table: bestseller
|
<text text-anchor="start" x="50.38" y="-185.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• rows: (book)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="50.38" y="-214.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
arity 1
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="50.38" y="-185.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
rows: (book)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- bestseller_rel -->
|
<!-- bestseller_rel -->
|
||||||
<g id="node5" class="node">
|
<g id="node5" class="node">
|
||||||
<title>bestseller_rel</title>
|
<title>bestseller_rel</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M476.12,-264.12C476.12,-264.12 365.88,-264.12 365.88,-264.12 359.88,-264.12 353.88,-258.12 353.88,-252.12 353.88,-252.12 353.88,-181.88 353.88,-181.88 353.88,-175.88 359.88,-169.88 365.88,-169.88 365.88,-169.88 476.12,-169.88 476.12,-169.88 482.12,-169.88 488.12,-175.88 488.12,-181.88 488.12,-181.88 488.12,-252.12 488.12,-252.12 488.12,-258.12 482.12,-264.12 476.12,-264.12"/>
|
||||||
d="M476.12,-264.12C476.12,-264.12 365.88,-264.12 365.88,-264.12 359.88,-264.12 353.88,-258.12 353.88,-252.12 353.88,-252.12 353.88,-181.88 353.88,-181.88 353.88,-175.88 359.88,-169.88 365.88,-169.88 365.88,-169.88 476.12,-169.88 476.12,-169.88 482.12,-169.88 488.12,-175.88 488.12,-181.88 488.12,-181.88 488.12,-252.12 488.12,-252.12 488.12,-258.12 482.12,-264.12 476.12,-264.12"/>
|
<text text-anchor="start" x="377.5" y="-243.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">bestseller_rel</text>
|
||||||
<text text-anchor="start" x="377.5" y="-243.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="365.88" y="-214.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">pattern: [Var book]</text>
|
||||||
font-size="14.00">bestseller_rel
|
<text text-anchor="start" x="365.88" y="-185.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">cols: [book]</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="365.88" y="-214.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
pattern: [Var book]
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="365.88" y="-185.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
cols: [book]
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- bestseller_table->bestseller_rel -->
|
<!-- bestseller_table->bestseller_rel -->
|
||||||
<g id="edge2" class="edge">
|
<g id="edge2" class="edge">
|
||||||
<title>bestseller_table->bestseller_rel</title>
|
<title>bestseller_table->bestseller_rel</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M168.53,-217C218.65,-217 288.47,-217 341.83,-217"/>
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M168.53,-217C218.65,-217 288.47,-217 341.83,-217"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="341.82,-220.5 351.82,-217 341.82,-213.5 341.82,-220.5"/>
|
||||||
points="341.82,-220.5 351.82,-217 341.82,-213.5 341.82,-220.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- price_table -->
|
<!-- price_table -->
|
||||||
<g id="node3" class="node">
|
<g id="node3" class="node">
|
||||||
<title>price_table</title>
|
<title>price_table</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M168.5,-120.12C168.5,-120.12 38,-120.12 38,-120.12 32,-120.12 26,-114.12 26,-108.12 26,-108.12 26,-37.88 26,-37.88 26,-31.88 32,-25.88 38,-25.88 38,-25.88 168.5,-25.88 168.5,-25.88 174.5,-25.88 180.5,-31.88 180.5,-37.88 180.5,-37.88 180.5,-108.12 180.5,-108.12 180.5,-114.12 174.5,-120.12 168.5,-120.12"/>
|
||||||
d="M168.5,-120.12C168.5,-120.12 38,-120.12 38,-120.12 32,-120.12 26,-114.12 26,-108.12 26,-108.12 26,-37.88 26,-37.88 26,-31.88 32,-25.88 38,-25.88 38,-25.88 168.5,-25.88 168.5,-25.88 174.5,-25.88 180.5,-31.88 180.5,-37.88 180.5,-37.88 180.5,-108.12 180.5,-108.12 180.5,-114.12 174.5,-120.12 168.5,-120.12"/>
|
<text text-anchor="start" x="65.75" y="-99.83" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Table: price</text>
|
||||||
<text text-anchor="start" x="65.75" y="-99.83" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="38" y="-70.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• arity 2</text>
|
||||||
font-size="14.00">Table: price
|
<text text-anchor="start" x="38" y="-41.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• rows: (book, dollars)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="38" y="-70.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
arity 2
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="38" y="-41.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
rows: (book, dollars)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- price_rel -->
|
<!-- price_rel -->
|
||||||
<g id="node6" class="node">
|
<g id="node6" class="node">
|
||||||
<title>price_rel</title>
|
<title>price_rel</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M511.75,-120.12C511.75,-120.12 330.25,-120.12 330.25,-120.12 324.25,-120.12 318.25,-114.12 318.25,-108.12 318.25,-108.12 318.25,-37.88 318.25,-37.88 318.25,-31.88 324.25,-25.88 330.25,-25.88 330.25,-25.88 511.75,-25.88 511.75,-25.88 517.75,-25.88 523.75,-31.88 523.75,-37.88 523.75,-37.88 523.75,-108.12 523.75,-108.12 523.75,-114.12 517.75,-120.12 511.75,-120.12"/>
|
||||||
d="M511.75,-120.12C511.75,-120.12 330.25,-120.12 330.25,-120.12 324.25,-120.12 318.25,-114.12 318.25,-108.12 318.25,-108.12 318.25,-37.88 318.25,-37.88 318.25,-31.88 324.25,-25.88 330.25,-25.88 330.25,-25.88 511.75,-25.88 511.75,-25.88 517.75,-25.88 523.75,-31.88 523.75,-37.88 523.75,-37.88 523.75,-108.12 523.75,-108.12 523.75,-114.12 517.75,-120.12 511.75,-120.12"/>
|
<text text-anchor="start" x="392.88" y="-99.83" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">price_rel</text>
|
||||||
<text text-anchor="start" x="392.88" y="-99.83" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="330.25" y="-70.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">pattern: [Var book, Var dollars]</text>
|
||||||
font-size="14.00">price_rel
|
<text text-anchor="start" x="330.25" y="-41.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">cols: [book, dollars]</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="330.25" y="-70.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
pattern: [Var book, Var dollars]
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="330.25" y="-41.58" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
cols: [book, dollars]
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- price_table->price_rel -->
|
<!-- price_table->price_rel -->
|
||||||
<g id="edge3" class="edge">
|
<g id="edge3" class="edge">
|
||||||
<title>price_table->price_rel</title>
|
<title>price_table->price_rel</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M180.68,-73C218.39,-73 264.62,-73 306.37,-73"/>
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M180.68,-73C218.39,-73 264.62,-73 306.37,-73"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="306.2,-76.5 316.2,-73 306.2,-69.5 306.2,-76.5"/>
|
||||||
points="306.2,-76.5 316.2,-73 306.2,-69.5 306.2,-76.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- semijoin_step -->
|
<!-- semijoin_step -->
|
||||||
<g id="node7" class="node">
|
<g id="node7" class="node">
|
||||||
<title>semijoin_step</title>
|
<title>semijoin_step</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M819.75,-278.62C819.75,-278.62 691.5,-278.62 691.5,-278.62 685.5,-278.62 679.5,-272.62 679.5,-266.62 679.5,-266.62 679.5,-167.38 679.5,-167.38 679.5,-161.38 685.5,-155.38 691.5,-155.38 691.5,-155.38 819.75,-155.38 819.75,-155.38 825.75,-155.38 831.75,-161.38 831.75,-167.38 831.75,-167.38 831.75,-266.62 831.75,-266.62 831.75,-272.62 825.75,-278.62 819.75,-278.62"/>
|
||||||
d="M819.75,-278.62C819.75,-278.62 691.5,-278.62 691.5,-278.62 685.5,-278.62 679.5,-272.62 679.5,-266.62 679.5,-266.62 679.5,-167.38 679.5,-167.38 679.5,-161.38 685.5,-155.38 691.5,-155.38 691.5,-155.38 819.75,-155.38 819.75,-155.38 825.75,-155.38 831.75,-161.38 831.75,-167.38 831.75,-167.38 831.75,-266.62 831.75,-266.62 831.75,-272.62 825.75,-278.62 819.75,-278.62"/>
|
<text text-anchor="start" x="727.88" y="-258.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">semijoin</text>
|
||||||
<text text-anchor="start" x="727.88" y="-258.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="691.5" y="-229.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">authors of bestsellers</text>
|
||||||
font-size="14.00">semijoin
|
<text text-anchor="start" x="691.5" y="-200.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">shared: book</text>
|
||||||
</text>
|
<text text-anchor="start" x="691.5" y="-171.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">cols: [name, book]</text>
|
||||||
<text text-anchor="start" x="691.5" y="-229.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
authors of bestsellers
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="691.5" y="-200.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
shared: book
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="691.5" y="-171.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
cols: [name, book]
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- author_rel->semijoin_step -->
|
<!-- author_rel->semijoin_step -->
|
||||||
<g id="edge4" class="edge">
|
<g id="edge4" class="edge">
|
||||||
<title>author_rel->semijoin_step</title>
|
<title>author_rel->semijoin_step</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M521.48,-324.79C550.11,-313.83 581.24,-301.4 609.5,-289 628.84,-280.51 649.32,-270.81 668.61,-261.33"/>
|
||||||
d="M521.48,-324.79C550.11,-313.83 581.24,-301.4 609.5,-289 628.84,-280.51 649.32,-270.81 668.61,-261.33"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="670.15,-264.48 677.56,-256.91 667.04,-258.2 670.15,-264.48"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
<text text-anchor="middle" x="637.5" y="-284.9" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">left</text>
|
||||||
points="670.15,-264.48 677.56,-256.91 667.04,-258.2 670.15,-264.48"/>
|
|
||||||
<text text-anchor="middle" x="637.5" y="-284.9" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">left
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- bestseller_rel->semijoin_step -->
|
<!-- bestseller_rel->semijoin_step -->
|
||||||
<g id="edge5" class="edge">
|
<g id="edge5" class="edge">
|
||||||
<title>bestseller_rel->semijoin_step</title>
|
<title>bestseller_rel->semijoin_step</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M488.51,-217C539.93,-217 611.54,-217 667.53,-217"/>
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M488.51,-217C539.93,-217 611.54,-217 667.53,-217"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="667.41,-220.5 677.41,-217 667.41,-213.5 667.41,-220.5"/>
|
||||||
points="667.41,-220.5 677.41,-217 667.41,-213.5 667.41,-220.5"/>
|
<text text-anchor="middle" x="637.5" y="-221.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">right</text>
|
||||||
<text text-anchor="middle" x="637.5" y="-221.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">right
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- natural_join_step -->
|
<!-- natural_join_step -->
|
||||||
<g id="node8" class="node">
|
<g id="node8" class="node">
|
||||||
<title>natural_join_step</title>
|
<title>natural_join_step</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1080,-278.62C1080,-278.62 922.5,-278.62 922.5,-278.62 916.5,-278.62 910.5,-272.62 910.5,-266.62 910.5,-266.62 910.5,-167.38 910.5,-167.38 910.5,-161.38 916.5,-155.38 922.5,-155.38 922.5,-155.38 1080,-155.38 1080,-155.38 1086,-155.38 1092,-161.38 1092,-167.38 1092,-167.38 1092,-266.62 1092,-266.62 1092,-272.62 1086,-278.62 1080,-278.62"/>
|
||||||
d="M1080,-278.62C1080,-278.62 922.5,-278.62 922.5,-278.62 916.5,-278.62 910.5,-272.62 910.5,-266.62 910.5,-266.62 910.5,-167.38 910.5,-167.38 910.5,-161.38 916.5,-155.38 922.5,-155.38 922.5,-155.38 1080,-155.38 1080,-155.38 1086,-155.38 1092,-161.38 1092,-167.38 1092,-167.38 1092,-266.62 1092,-266.62 1092,-272.62 1086,-278.62 1080,-278.62"/>
|
<text text-anchor="start" x="963" y="-258.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">natural_join</text>
|
||||||
<text text-anchor="start" x="963" y="-258.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="922.5" y="-229.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">attach each book's price</text>
|
||||||
font-size="14.00">natural_join
|
<text text-anchor="start" x="922.5" y="-200.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">shared: book</text>
|
||||||
</text>
|
<text text-anchor="start" x="922.5" y="-171.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">cols: [name, book, dollars]</text>
|
||||||
<text text-anchor="start" x="922.5" y="-229.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
attach each book's price
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="922.5" y="-200.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
shared: book
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="922.5" y="-171.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
cols: [name, book, dollars]
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- price_rel->natural_join_step -->
|
<!-- price_rel->natural_join_step -->
|
||||||
<g id="edge7" class="edge">
|
<g id="edge7" class="edge">
|
||||||
<title>price_rel->natural_join_step</title>
|
<title>price_rel->natural_join_step</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M523.91,-71.78C608.41,-73.58 730.63,-82.79 831.75,-116.5 855.71,-124.49 879.92,-136.28 902.24,-149"/>
|
||||||
d="M523.91,-71.78C608.41,-73.58 730.63,-82.79 831.75,-116.5 855.71,-124.49 879.92,-136.28 902.24,-149"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="900.38,-151.97 910.78,-153.98 903.91,-145.92 900.38,-151.97"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
<text text-anchor="middle" x="755.62" y="-121.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">right</text>
|
||||||
points="900.38,-151.97 910.78,-153.98 903.91,-145.92 900.38,-151.97"/>
|
|
||||||
<text text-anchor="middle" x="755.62" y="-121.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">right
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- semijoin_step->natural_join_step -->
|
<!-- semijoin_step->natural_join_step -->
|
||||||
<g id="edge6" class="edge">
|
<g id="edge6" class="edge">
|
||||||
<title>semijoin_step->natural_join_step</title>
|
<title>semijoin_step->natural_join_step</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M832.04,-217C853.1,-217 876.34,-217 898.65,-217"/>
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M832.04,-217C853.1,-217 876.34,-217 898.65,-217"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="898.4,-220.5 908.4,-217 898.4,-213.5 898.4,-220.5"/>
|
||||||
points="898.4,-220.5 908.4,-217 898.4,-213.5 898.4,-220.5"/>
|
<text text-anchor="middle" x="871.12" y="-221.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">left</text>
|
||||||
<text text-anchor="middle" x="871.12" y="-221.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">left
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- result -->
|
<!-- result -->
|
||||||
<g id="node9" class="node">
|
<g id="node9" class="node">
|
||||||
<title>result</title>
|
<title>result</title>
|
||||||
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5"
|
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5" d="M1435.75,-264.12C1435.75,-264.12 1171,-264.12 1171,-264.12 1165,-264.12 1159,-258.12 1159,-252.12 1159,-252.12 1159,-181.88 1159,-181.88 1159,-175.88 1165,-169.88 1171,-169.88 1171,-169.88 1435.75,-169.88 1435.75,-169.88 1441.75,-169.88 1447.75,-175.88 1447.75,-181.88 1447.75,-181.88 1447.75,-252.12 1447.75,-252.12 1447.75,-258.12 1441.75,-264.12 1435.75,-264.12"/>
|
||||||
d="M1435.75,-264.12C1435.75,-264.12 1171,-264.12 1171,-264.12 1165,-264.12 1159,-258.12 1159,-252.12 1159,-252.12 1159,-181.88 1159,-181.88 1159,-175.88 1165,-169.88 1171,-169.88 1171,-169.88 1435.75,-169.88 1435.75,-169.88 1441.75,-169.88 1447.75,-175.88 1447.75,-181.88 1447.75,-181.88 1447.75,-252.12 1447.75,-252.12 1447.75,-258.12 1441.75,-264.12 1435.75,-264.12"/>
|
<text text-anchor="start" x="1277.5" y="-243.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Q result</text>
|
||||||
<text text-anchor="start" x="1277.5" y="-243.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="1171" y="-214.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">authors of bestsellers with each book's price</text>
|
||||||
font-size="14.00">Q result
|
<text text-anchor="start" x="1171" y="-185.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">cols: [name, book, dollars]</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1171" y="-214.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
authors of bestsellers with each book's price
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1171" y="-185.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
cols: [name, book, dollars]
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- natural_join_step->result -->
|
<!-- natural_join_step->result -->
|
||||||
<g id="edge8" class="edge">
|
<g id="edge8" class="edge">
|
||||||
<title>natural_join_step->result</title>
|
<title>natural_join_step->result</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M1092.3,-217C1109.6,-217 1128.17,-217 1146.86,-217"/>
|
||||||
d="M1092.3,-217C1109.6,-217 1128.17,-217 1146.86,-217"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1146.69,-220.5 1156.69,-217 1146.69,-213.5 1146.69,-220.5"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="1146.69,-220.5 1156.69,-217 1146.69,-213.5 1146.69,-220.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 14 KiB |
@ -2,9 +2,9 @@
|
|||||||
//!
|
//!
|
||||||
//! Three operators are in scope:
|
//! Three operators are in scope:
|
||||||
//!
|
//!
|
||||||
//! - [`atom::scan_atom`] scans a [`Table`](storage::table::Table) under
|
//! - [`atom::scan_atom`] scans a [`Table`] under an [`atom::AtomPattern`],
|
||||||
//! an [`atom::AtomPattern`], filtering for repeated-variable equality and
|
//! filtering for repeated-variable equality and literal equality, and
|
||||||
//! literal equality, and outputs a binding [`relation::Relation`].
|
//! outputs a binding [`relation::Relation`].
|
||||||
//! - [`join::semijoin`] keeps rows of one relation whose shared-column values
|
//! - [`join::semijoin`] keeps rows of one relation whose shared-column values
|
||||||
//! appear in another.
|
//! appear in another.
|
||||||
//! - [`join::natural_join`] combines rows that agree on shared columns,
|
//! - [`join::natural_join`] combines rows that agree on shared columns,
|
||||||
@ -14,10 +14,8 @@
|
|||||||
//! is just an expression like
|
//! is just an expression like
|
||||||
//! `natural_join(&semijoin(&a, &b), &scan_atom(&t, &p))`.
|
//! `natural_join(&semijoin(&a, &b), &scan_atom(&t, &p))`.
|
||||||
//!
|
//!
|
||||||
//! Foundational types [`Value`](storage::value::Value) and
|
//! `Value` and `Table` live in the `storage` crate; consumers that build
|
||||||
//! [`Table`](storage::table::Table) live in `storage`, the
|
//! inputs depend on `storage` directly.
|
||||||
//! storage-layer crate this crate is built on; storage backends produce
|
|
||||||
//! `Table`s that operators here consume.
|
|
||||||
|
|
||||||
pub mod atom;
|
pub mod atom;
|
||||||
pub mod join;
|
pub mod join;
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
//! bestsellers via a semijoin against `bestseller`, then attaches each book's
|
//! bestsellers via a semijoin against `bestseller`, then attaches each book's
|
||||||
//! price via a natural join against `price`.
|
//! price via a natural join against `price`.
|
||||||
|
|
||||||
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
use query_ops::atom::{scan_atom, AtomPattern, Term};
|
||||||
use query_ops::join::{natural_join, semijoin};
|
use query_ops::join::{natural_join, semijoin};
|
||||||
use storage::table::Table;
|
use storage::table::Table;
|
||||||
use storage::value::Value;
|
use storage::value::Value;
|
||||||
|
|||||||
@ -4,10 +4,10 @@
|
|||||||
//! Demonstrates that `query-ops` operators can consume from a storage backend
|
//! Demonstrates that `query-ops` operators can consume from a storage backend
|
||||||
//! through the [`scan_as_table`] bridge, with no changes to `query-ops` itself.
|
//! through the [`scan_as_table`] bridge, with no changes to `query-ops` itself.
|
||||||
|
|
||||||
use query_ops::atom::{AtomPattern, Term, scan_atom};
|
use query_ops::atom::{scan_atom, AtomPattern, Term};
|
||||||
use storage::table::Table;
|
use storage::table::Table;
|
||||||
use storage::value::Value;
|
use storage::value::Value;
|
||||||
use storage::{MemoryStorage, Storage, StorageError, scan_as_table};
|
use storage::{scan_as_table, MemoryStorage, Storage, StorageError};
|
||||||
|
|
||||||
fn i(x: i64) -> Value {
|
fn i(x: i64) -> Value {
|
||||||
Value::Int(x)
|
Value::Int(x)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ This crates helps with decoupling the query execution logic from the underlying
|
|||||||
### Public API
|
### Public API
|
||||||
|
|
||||||
| Item | Kind | Description |
|
| Item | Kind | Description |
|
||||||
|--------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `Storage` | trait | Backend-agnostic interface for storing and retrieving rows. Required methods: `create_relation`, `arity`, `scan_iter`, and `transaction`. The rest (`scan`, `scan_where`, `insert`, `delete`) have default implementations. |
|
| `Storage` | trait | Backend-agnostic interface for storing and retrieving rows. Required methods: `create_relation`, `arity`, `scan_iter`, and `transaction`. The rest (`scan`, `scan_where`, `insert`, `delete`) have default implementations. |
|
||||||
| `Transaction` | trait | Atomic batch of inserts and deletes against a `Storage`. `insert` returns a pending `RowId`; `commit` consumes the boxed transaction and returns a `CommittedTx`; dropping without committing rolls back. |
|
| `Transaction` | trait | Atomic batch of inserts and deletes against a `Storage`. `insert` returns a pending `RowId`; `commit` consumes the boxed transaction and returns a `CommittedTx`; dropping without committing rolls back. |
|
||||||
| `CommittedTx` | struct | Result of a successful `Transaction::commit`. Resolves pending `RowId`s returned during the transaction to their post-commit form via `resolve`. Empty for KV adapters where pending equals real; populated for `geomerge`. |
|
| `CommittedTx` | struct | Result of a successful `Transaction::commit`. Resolves pending `RowId`s returned during the transaction to their post-commit form via `resolve`. Empty for KV adapters where pending equals real; populated for `geomerge`. |
|
||||||
@ -24,7 +24,8 @@ This crates helps with decoupling the query execution logic from the underlying
|
|||||||
| `adapters::redb::RedbStorage` | struct (feat) | Single-file B-tree backed `Storage`, behind the `redb` feature. Wraps `redb::WriteTransaction` for native atomic commits. |
|
| `adapters::redb::RedbStorage` | struct (feat) | Single-file B-tree backed `Storage`, behind the `redb` feature. Wraps `redb::WriteTransaction` for native atomic commits. |
|
||||||
| `adapters::fjall::FjallStorage` | struct (feat) | LSM-tree backed `Storage`, behind the `fjall` feature. Each relation gets a partition; transactions buffer inserts and apply them on commit. |
|
| `adapters::fjall::FjallStorage` | struct (feat) | LSM-tree backed `Storage`, behind the `fjall` feature. Each relation gets a partition; transactions buffer inserts and apply them on commit. |
|
||||||
| `adapters::lmdb::LmdbStorage` | struct (feat) | mmap'd B-tree backed `Storage`, behind the `lmdb` feature. Wraps `heed`'s `RwTxn` for native atomic commits. |
|
| `adapters::lmdb::LmdbStorage` | struct (feat) | mmap'd B-tree backed `Storage`, behind the `lmdb` feature. Wraps `heed`'s `RwTxn` for native atomic commits. |
|
||||||
| `adapters::geomerge::GeomergeStorage` | struct (feat) | CRDT-backed `Storage` over the workspace's `geomerge` crate, behind the `geomerge` feature. Wraps `geomerge::Transaction` and resolves pending row IDs via `CommittedTx`. Deletion is not supported (append-only log). |
|
| `adapters::geomerge::GeomergeStorage` | struct (feat) | CRDT-backed `Storage` over the workspace's `geomerge` crate, behind the `geomerge` feature. Wraps `geomerge::Transaction` and resolves pending row IDs via `CommittedTx`. Deletion is not supported (append-only log). Construct with `from_theory`, `from_store`, or `with_relations` (synthesizes a theory from `(name, Vec<ColumnKind>)` for callers that lack a typed schema). |
|
||||||
|
| `adapters::geomerge::ColumnKind` | enum (feat) | Primitive column type fed to `GeomergeStorage::with_relations`: `Int` maps to geomerge `PrimInt`, `String` maps to `PrimString`. Exists so callers can synthesize a theory without depending on `geolog-lang::ir` directly. |
|
||||||
|
|
||||||
Data types and their relationships:
|
Data types and their relationships:
|
||||||
|
|
||||||
@ -99,7 +100,8 @@ cargo test -p storage --all-features
|
|||||||
iterators.
|
iterators.
|
||||||
- **Atomic transactions.**
|
- **Atomic transactions.**
|
||||||
For storage backends with write transactions support (LMDB, Redb, SQLite, and geomerge) we use their transaction API directly.
|
For storage backends with write transactions support (LMDB, Redb, SQLite, and geomerge) we use their transaction API directly.
|
||||||
Adapters without native transaction support (MemoryStorage and Fjall) implement `Transaction` with an internal buffer of pending operations that are applied on `commit`.
|
Adapters without native transaction support (MemoryStorage and Fjall) implement `Transaction` with an internal buffer of pending operations that are
|
||||||
|
applied on `commit`.
|
||||||
Note that dropping a transaction without calling `commit` rolls back any pending operations.
|
Note that dropping a transaction without calling `commit` rolls back any pending operations.
|
||||||
- **Deletion support.**
|
- **Deletion support.**
|
||||||
Most adapters implement `delete`.
|
Most adapters implement `delete`.
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
use criterion::{BatchSize, BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
|
||||||
use storage::value::Value;
|
use storage::value::Value;
|
||||||
use storage::{MemoryStorage, Storage};
|
use storage::{MemoryStorage, Storage};
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
use criterion::{BatchSize, BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
|
||||||
use geomerge::ir::FlatTheory;
|
use geomerge::ir::FlatTheory;
|
||||||
use storage::adapters::geomerge::GeomergeStorage;
|
use storage::adapters::geomerge::GeomergeStorage;
|
||||||
use storage::id::RowId;
|
use storage::id::RowId;
|
||||||
|
|||||||
@ -5,370 +5,196 @@
|
|||||||
-->
|
-->
|
||||||
<!-- Title: StorageTypes Pages: 1 -->
|
<!-- Title: StorageTypes Pages: 1 -->
|
||||||
<svg width="836pt" height="1114pt"
|
<svg width="836pt" height="1114pt"
|
||||||
viewBox="0.00 0.00 835.88 1114.00" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0.00 0.00 835.88 1114.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1110)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1110)">
|
||||||
<title>StorageTypes</title>
|
<title>StorageTypes</title>
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-1110 831.88,-1110 831.88,4 -4,4"/>
|
<polygon fill="white" stroke="none" points="-4,4 -4,-1110 831.88,-1110 831.88,4 -4,4"/>
|
||||||
<!-- storage_node -->
|
<!-- storage_node -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
<title>storage_node</title>
|
<title>storage_node</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M439.38,-802.5C439.38,-802.5 180.63,-802.5 180.63,-802.5 174.63,-802.5 168.63,-796.5 168.63,-790.5 168.63,-790.5 168.63,-545.5 168.63,-545.5 168.63,-539.5 174.63,-533.5 180.63,-533.5 180.63,-533.5 439.38,-533.5 439.38,-533.5 445.38,-533.5 451.38,-539.5 451.38,-545.5 451.38,-545.5 451.38,-790.5 451.38,-790.5 451.38,-796.5 445.38,-802.5 439.38,-802.5"/>
|
||||||
d="M439.38,-802.5C439.38,-802.5 180.63,-802.5 180.63,-802.5 174.63,-802.5 168.63,-796.5 168.63,-790.5 168.63,-790.5 168.63,-545.5 168.63,-545.5 168.63,-539.5 174.63,-533.5 180.63,-533.5 180.63,-533.5 439.38,-533.5 439.38,-533.5 445.38,-533.5 451.38,-539.5 451.38,-545.5 451.38,-545.5 451.38,-790.5 451.38,-790.5 451.38,-796.5 445.38,-802.5 439.38,-802.5"/>
|
<text text-anchor="start" x="265.01" y="-782.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Storage</text>
|
||||||
<text text-anchor="start" x="265.01" y="-782.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="316.01" y="-782.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (trait)</text>
|
||||||
font-size="14.00">Storage
|
<text text-anchor="start" x="180.63" y="-752.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">create_relation(name, arity)</text>
|
||||||
</text>
|
<text text-anchor="start" x="180.63" y="-723.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">arity(name)</text>
|
||||||
<text text-anchor="start" x="316.01" y="-782.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="180.63" y="-694.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">scan_iter(name) -> RowStream</text>
|
||||||
 (trait)
|
<text text-anchor="start" x="180.63" y="-665.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">scan(name) -> Vec<(RowId, Vec<Value>)></text>
|
||||||
</text>
|
<text text-anchor="start" x="180.63" y="-636.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">scan_where(name, col, value)</text>
|
||||||
<text text-anchor="start" x="180.63" y="-752.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="180.63" y="-607.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">transaction() -> Box<dyn Transaction></text>
|
||||||
create_relation(name, arity)
|
<text text-anchor="start" x="180.63" y="-578.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">insert(name, row) -> RowId</text>
|
||||||
</text>
|
<text text-anchor="start" x="180.63" y="-549.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">delete(name, id)</text>
|
||||||
<text text-anchor="start" x="180.63" y="-723.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
arity(name)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="180.63" y="-694.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
scan_iter(name) -> RowStream
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="180.63" y="-665.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
scan(name) -> Vec<(RowId, Vec<Value>)>
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="180.63" y="-636.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
scan_where(name, col, value)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="180.63" y="-607.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
transaction() -> Box<dyn Transaction>
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="180.63" y="-578.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
insert(name, row) -> RowId
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="180.63" y="-549.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
delete(name, id)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- transaction_node -->
|
<!-- transaction_node -->
|
||||||
<g id="node2" class="node">
|
<g id="node2" class="node">
|
||||||
<title>transaction_node</title>
|
<title>transaction_node</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M245.51,-470C245.51,-470 80.51,-470 80.51,-470 74.51,-470 68.51,-464 68.51,-458 68.51,-458 68.51,-358 68.51,-358 68.51,-352 74.51,-346 80.51,-346 80.51,-346 245.51,-346 245.51,-346 251.51,-346 257.51,-352 257.51,-358 257.51,-358 257.51,-458 257.51,-458 257.51,-464 251.51,-470 245.51,-470"/>
|
||||||
d="M245.51,-470C245.51,-470 80.51,-470 80.51,-470 74.51,-470 68.51,-464 68.51,-458 68.51,-458 68.51,-358 68.51,-358 68.51,-352 74.51,-346 80.51,-346 80.51,-346 245.51,-346 245.51,-346 251.51,-346 257.51,-352 257.51,-358 257.51,-358 257.51,-458 257.51,-458 257.51,-464 251.51,-470 245.51,-470"/>
|
<text text-anchor="start" x="105.63" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Transaction</text>
|
||||||
<text text-anchor="start" x="105.63" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="181.38" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (trait)</text>
|
||||||
font-size="14.00">Transaction
|
<text text-anchor="start" x="80.51" y="-419.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">insert(name, row) -> RowId</text>
|
||||||
</text>
|
<text text-anchor="start" x="80.51" y="-390.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">delete(name, id)</text>
|
||||||
<text text-anchor="start" x="181.38" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="80.51" y="-361.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">commit() -> CommittedTx</text>
|
||||||
 (trait)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="80.51" y="-419.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
insert(name, row) -> RowId
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="80.51" y="-390.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
delete(name, id)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="80.51" y="-361.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
commit() -> CommittedTx
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- storage_node->transaction_node -->
|
<!-- storage_node->transaction_node -->
|
||||||
<g id="edge2" class="edge">
|
<g id="edge2" class="edge">
|
||||||
<title>storage_node->transaction_node</title>
|
<title>storage_node->transaction_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M233.71,-533.09C223.34,-514.9 213.06,-496.84 203.67,-480.36"/>
|
||||||
d="M233.71,-533.09C223.34,-514.9 213.06,-496.84 203.67,-480.36"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="206.83,-478.85 198.84,-471.9 200.75,-482.32 206.83,-478.85"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="257.64" y="-499.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">transaction() yields</text>
|
||||||
points="206.83,-478.85 198.84,-471.9 200.75,-482.32 206.83,-478.85"/>
|
|
||||||
<text text-anchor="middle" x="257.64" y="-499.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">transaction() yields
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- row_stream_node -->
|
<!-- row_stream_node -->
|
||||||
<g id="node4" class="node">
|
<g id="node4" class="node">
|
||||||
<title>row_stream_node</title>
|
<title>row_stream_node</title>
|
||||||
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5"
|
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5" d="M594.26,-470C594.26,-470 319.76,-470 319.76,-470 313.76,-470 307.76,-464 307.76,-458 307.76,-458 307.76,-358 307.76,-358 307.76,-352 313.76,-346 319.76,-346 319.76,-346 594.26,-346 594.26,-346 600.26,-346 606.26,-352 606.26,-358 606.26,-358 606.26,-458 606.26,-458 606.26,-464 600.26,-470 594.26,-470"/>
|
||||||
d="M594.26,-470C594.26,-470 319.76,-470 319.76,-470 313.76,-470 307.76,-464 307.76,-458 307.76,-458 307.76,-358 307.76,-358 307.76,-352 313.76,-346 319.76,-346 319.76,-346 594.26,-346 594.26,-346 600.26,-346 606.26,-352 606.26,-358 606.26,-358 606.26,-458 606.26,-458 606.26,-464 600.26,-470 594.26,-470"/>
|
<text text-anchor="start" x="368.88" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">RowStream<'a></text>
|
||||||
<text text-anchor="start" x="368.88" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="471.63" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (type alias)</text>
|
||||||
font-size="14.00">RowStream<'a>
|
<text text-anchor="start" x="319.76" y="-419.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Box<dyn Iterator<Item =</text>
|
||||||
</text>
|
<text text-anchor="start" x="319.76" y="-390.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  Result<(RowId, Vec<Value>), StorageError></text>
|
||||||
<text text-anchor="start" x="471.63" y="-449.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="319.76" y="-361.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">> + 'a></text>
|
||||||
 (type alias)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="319.76" y="-419.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Box<dyn Iterator<Item =
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="319.76" y="-390.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 Result<(RowId, Vec<Value>), StorageError>
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="319.76" y="-361.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
> + 'a>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- storage_node->row_stream_node -->
|
<!-- storage_node->row_stream_node -->
|
||||||
<g id="edge5" class="edge">
|
<g id="edge5" class="edge">
|
||||||
<title>storage_node->row_stream_node</title>
|
<title>storage_node->row_stream_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M386.3,-533.09C396.67,-514.9 406.96,-496.84 416.34,-480.36"/>
|
||||||
d="M386.3,-533.09C396.67,-514.9 406.96,-496.84 416.34,-480.36"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="419.26,-482.32 421.17,-471.9 413.18,-478.85 419.26,-482.32"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="437.14" y="-499.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">scan_iter yields</text>
|
||||||
points="419.26,-482.32 421.17,-471.9 413.18,-478.85 419.26,-482.32"/>
|
|
||||||
<text text-anchor="middle" x="437.14" y="-499.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">scan_iter yields
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- committed_tx_node -->
|
<!-- committed_tx_node -->
|
||||||
<g id="node3" class="node">
|
<g id="node3" class="node">
|
||||||
<title>committed_tx_node</title>
|
<title>committed_tx_node</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M344.88,-268C344.88,-268 113.13,-268 113.13,-268 107.13,-268 101.13,-262 101.13,-256 101.13,-256 101.13,-185 101.13,-185 101.13,-179 107.13,-173 113.13,-173 113.13,-173 344.88,-173 344.88,-173 350.88,-173 356.88,-179 356.88,-185 356.88,-185 356.88,-256 356.88,-256 356.88,-262 350.88,-268 344.88,-268"/>
|
||||||
d="M344.88,-268C344.88,-268 113.13,-268 113.13,-268 107.13,-268 101.13,-262 101.13,-256 101.13,-256 101.13,-185 101.13,-185 101.13,-179 107.13,-173 113.13,-173 113.13,-173 344.88,-173 344.88,-173 350.88,-173 356.88,-179 356.88,-185 356.88,-185 356.88,-256 356.88,-256 356.88,-262 350.88,-268 344.88,-268"/>
|
<text text-anchor="start" x="160.38" y="-247.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">CommittedTx</text>
|
||||||
<text text-anchor="start" x="160.38" y="-247.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="248.13" y="-247.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (struct)</text>
|
||||||
font-size="14.00">CommittedTx
|
<text text-anchor="start" x="113.13" y="-217.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">resolutions: HashMap<RowId, RowId></text>
|
||||||
</text>
|
<text text-anchor="start" x="113.13" y="-188.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">resolve(pending) -> RowId</text>
|
||||||
<text text-anchor="start" x="248.13" y="-247.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (struct)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="113.13" y="-217.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
resolutions: HashMap<RowId, RowId>
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="113.13" y="-188.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
resolve(pending) -> RowId
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- transaction_node->committed_tx_node -->
|
<!-- transaction_node->committed_tx_node -->
|
||||||
<g id="edge3" class="edge">
|
<g id="edge3" class="edge">
|
||||||
<title>transaction_node->committed_tx_node</title>
|
<title>transaction_node->committed_tx_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M184.8,-345.75C192.41,-324.37 200.91,-300.45 208.42,-279.36"/>
|
||||||
d="M184.8,-345.75C192.41,-324.37 200.91,-300.45 208.42,-279.36"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="211.69,-280.6 211.75,-270 205.1,-278.25 211.69,-280.6"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="228.73" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">commit() yields</text>
|
||||||
points="211.69,-280.6 211.75,-270 205.1,-278.25 211.69,-280.6"/>
|
|
||||||
<text text-anchor="middle" x="228.73" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">commit() yields
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- row_id_node -->
|
<!-- row_id_node -->
|
||||||
<g id="node5" class="node">
|
<g id="node5" class="node">
|
||||||
<title>row_id_node</title>
|
<title>row_id_node</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M501.63,-95C501.63,-95 298.38,-95 298.38,-95 292.38,-95 286.38,-89 286.38,-83 286.38,-83 286.38,-12 286.38,-12 286.38,-6 292.38,0 298.38,0 298.38,0 501.63,0 501.63,0 507.63,0 513.63,-6 513.63,-12 513.63,-12 513.63,-83 513.63,-83 513.63,-89 507.63,-95 501.63,-95"/>
|
||||||
d="M501.63,-95C501.63,-95 298.38,-95 298.38,-95 292.38,-95 286.38,-89 286.38,-83 286.38,-83 286.38,-12 286.38,-12 286.38,-6 292.38,0 298.38,0 298.38,0 501.63,0 501.63,0 507.63,0 513.63,-6 513.63,-12 513.63,-12 513.63,-83 513.63,-83 513.63,-89 507.63,-95 501.63,-95"/>
|
<text text-anchor="start" x="354.63" y="-74.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">RowId</text>
|
||||||
<text text-anchor="start" x="354.63" y="-74.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="395.88" y="-74.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (struct)</text>
|
||||||
font-size="14.00">RowId
|
<text text-anchor="start" x="298.38" y="-44.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">SmallVec<[u8; 36]> (opaque)</text>
|
||||||
</text>
|
<text text-anchor="start" x="298.38" y="-15.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">new(bytes), as_bytes(), from(u64)</text>
|
||||||
<text text-anchor="start" x="395.88" y="-74.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (struct)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="298.38" y="-44.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
SmallVec<[u8; 36]> (opaque)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="298.38" y="-15.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
new(bytes), as_bytes(), from(u64)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- transaction_node->row_id_node -->
|
<!-- transaction_node->row_id_node -->
|
||||||
<g id="edge8" class="edge">
|
<g id="edge8" class="edge">
|
||||||
<title>transaction_node->row_id_node</title>
|
<title>transaction_node->row_id_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M69.82,-345.56C50.18,-327.75 32.13,-306.52 21.26,-282.5 -1.47,-232.29 -11.84,-202.57 21.26,-158.5 52.05,-117.48 176.52,-86.8 274.48,-68.4"/>
|
||||||
d="M69.82,-345.56C50.18,-327.75 32.13,-306.52 21.26,-282.5 -1.47,-232.29 -11.84,-202.57 21.26,-158.5 52.05,-117.48 176.52,-86.8 274.48,-68.4"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="275,-71.87 284.19,-66.61 273.72,-64.98 275,-71.87"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="48.63" y="-218.7" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">insert() yields</text>
|
||||||
points="275,-71.87 284.19,-66.61 273.72,-64.98 275,-71.87"/>
|
|
||||||
<text text-anchor="middle" x="48.63" y="-218.7" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">insert() yields
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- committed_tx_node->row_id_node -->
|
<!-- committed_tx_node->row_id_node -->
|
||||||
<g id="edge4" class="edge">
|
<g id="edge4" class="edge">
|
||||||
<title>committed_tx_node->row_id_node</title>
|
<title>committed_tx_node->row_id_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M275.73,-172.77C297.13,-151.37 322.65,-125.86 344.97,-103.53"/>
|
||||||
d="M275.73,-172.77C297.13,-151.37 322.65,-125.86 344.97,-103.53"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="347.16,-106.3 351.75,-96.75 342.21,-101.35 347.16,-106.3"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="355.87" y="-124.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">resolve() yields</text>
|
||||||
points="347.16,-106.3 351.75,-96.75 342.21,-101.35 347.16,-106.3"/>
|
|
||||||
<text text-anchor="middle" x="355.87" y="-124.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">resolve() yields
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- row_stream_node->row_id_node -->
|
<!-- row_stream_node->row_id_node -->
|
||||||
<g id="edge6" class="edge">
|
<g id="edge6" class="edge">
|
||||||
<title>row_stream_node->row_id_node</title>
|
<title>row_stream_node->row_id_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M447.27,-345.78C436.64,-278.9 419.73,-172.56 409.28,-106.84"/>
|
||||||
d="M447.27,-345.78C436.64,-278.9 419.73,-172.56 409.28,-106.84"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="412.78,-106.58 407.76,-97.25 405.87,-107.68 412.78,-106.58"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="471.44" y="-218.7" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Item = (RowId, _)</text>
|
||||||
points="412.78,-106.58 407.76,-97.25 405.87,-107.68 412.78,-106.58"/>
|
|
||||||
<text text-anchor="middle" x="471.44" y="-218.7" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Item = (RowId, _)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- value_node -->
|
<!-- value_node -->
|
||||||
<g id="node6" class="node">
|
<g id="node6" class="node">
|
||||||
<title>value_node</title>
|
<title>value_node</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M675.51,-282.5C675.51,-282.5 588.51,-282.5 588.51,-282.5 582.51,-282.5 576.51,-276.5 576.51,-270.5 576.51,-270.5 576.51,-170.5 576.51,-170.5 576.51,-164.5 582.51,-158.5 588.51,-158.5 588.51,-158.5 675.51,-158.5 675.51,-158.5 681.51,-158.5 687.51,-164.5 687.51,-170.5 687.51,-170.5 687.51,-270.5 687.51,-270.5 687.51,-276.5 681.51,-282.5 675.51,-282.5"/>
|
||||||
d="M675.51,-282.5C675.51,-282.5 588.51,-282.5 588.51,-282.5 582.51,-282.5 576.51,-276.5 576.51,-270.5 576.51,-270.5 576.51,-170.5 576.51,-170.5 576.51,-164.5 582.51,-158.5 588.51,-158.5 588.51,-158.5 675.51,-158.5 675.51,-158.5 681.51,-158.5 687.51,-164.5 687.51,-170.5 687.51,-170.5 687.51,-270.5 687.51,-270.5 687.51,-276.5 681.51,-282.5 675.51,-282.5"/>
|
<text text-anchor="start" x="588.51" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Value</text>
|
||||||
<text text-anchor="start" x="588.51" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="624.51" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (enum)</text>
|
||||||
font-size="14.00">Value
|
<text text-anchor="start" x="588.51" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Int(i64)</text>
|
||||||
</text>
|
<text text-anchor="start" x="588.51" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Str(String)</text>
|
||||||
<text text-anchor="start" x="624.51" y="-262.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="588.51" y="-174.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Id(RowId)</text>
|
||||||
 (enum)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="588.51" y="-232.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Int(i64)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="588.51" y="-203.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Str(String)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="588.51" y="-174.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Id(RowId)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- row_stream_node->value_node -->
|
<!-- row_stream_node->value_node -->
|
||||||
<g id="edge7" class="edge">
|
<g id="edge7" class="edge">
|
||||||
<title>row_stream_node->value_node</title>
|
<title>row_stream_node->value_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M514.79,-345.75C531.89,-327.63 550.7,-307.69 568.15,-289.18"/>
|
||||||
d="M514.79,-345.75C531.89,-327.63 550.7,-307.69 568.15,-289.18"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="570.5,-291.79 574.82,-282.12 565.41,-286.99 570.5,-291.79"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="595.5" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Item = (_, Vec<Value>)</text>
|
||||||
points="570.5,-291.79 574.82,-282.12 565.41,-286.99 570.5,-291.79"/>
|
|
||||||
<text text-anchor="middle" x="595.5" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Item = (_, Vec<Value>)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- value_node->row_id_node -->
|
<!-- value_node->row_id_node -->
|
||||||
<g id="edge9" class="edge">
|
<g id="edge9" class="edge">
|
||||||
<title>value_node->row_id_node</title>
|
<title>value_node->row_id_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M576.14,-178.32C545.13,-155.47 506.2,-126.78 472.83,-102.18"/>
|
||||||
d="M576.14,-178.32C545.13,-155.47 506.2,-126.78 472.83,-102.18"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="475.17,-99.55 465.04,-96.44 471.01,-105.19 475.17,-99.55"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="528.92" y="-124.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Id(RowId)</text>
|
||||||
points="475.17,-99.55 465.04,-96.44 471.01,-105.19 475.17,-99.55"/>
|
|
||||||
<text text-anchor="middle" x="528.92" y="-124.95" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Id(RowId)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- table_node -->
|
<!-- table_node -->
|
||||||
<g id="node7" class="node">
|
<g id="node7" class="node">
|
||||||
<title>table_node</title>
|
<title>table_node</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M815.88,-455.5C815.88,-455.5 668.13,-455.5 668.13,-455.5 662.13,-455.5 656.13,-449.5 656.13,-443.5 656.13,-443.5 656.13,-372.5 656.13,-372.5 656.13,-366.5 662.13,-360.5 668.13,-360.5 668.13,-360.5 815.88,-360.5 815.88,-360.5 821.88,-360.5 827.88,-366.5 827.88,-372.5 827.88,-372.5 827.88,-443.5 827.88,-443.5 827.88,-449.5 821.88,-455.5 815.88,-455.5"/>
|
||||||
d="M815.88,-455.5C815.88,-455.5 668.13,-455.5 668.13,-455.5 662.13,-455.5 656.13,-449.5 656.13,-443.5 656.13,-443.5 656.13,-372.5 656.13,-372.5 656.13,-366.5 662.13,-360.5 668.13,-360.5 668.13,-360.5 815.88,-360.5 815.88,-360.5 821.88,-360.5 827.88,-366.5 827.88,-372.5 827.88,-372.5 827.88,-443.5 827.88,-443.5 827.88,-449.5 821.88,-455.5 815.88,-455.5"/>
|
<text text-anchor="start" x="700.01" y="-435.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Table</text>
|
||||||
<text text-anchor="start" x="700.01" y="-435.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="734.51" y="-435.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (struct)</text>
|
||||||
font-size="14.00">Table
|
<text text-anchor="start" x="668.13" y="-405.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">arity: usize</text>
|
||||||
</text>
|
<text text-anchor="start" x="668.13" y="-376.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">rows: Vec<Vec<Value>></text>
|
||||||
<text text-anchor="start" x="734.51" y="-435.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
 (struct)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="668.13" y="-405.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
arity: usize
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="668.13" y="-376.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
rows: Vec<Vec<Value>>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- table_node->value_node -->
|
<!-- table_node->value_node -->
|
||||||
<g id="edge10" class="edge">
|
<g id="edge10" class="edge">
|
||||||
<title>table_node->value_node</title>
|
<title>table_node->value_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M714.25,-360.19C702.11,-339.72 687.59,-315.24 674.17,-292.6"/>
|
||||||
d="M714.25,-360.19C702.11,-339.72 687.59,-315.24 674.17,-292.6"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="677.32,-291.06 669.21,-284.24 671.3,-294.63 677.32,-291.06"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="727.43" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Vec<Vec<Value>></text>
|
||||||
points="677.32,-291.06 669.21,-284.24 671.3,-294.63 677.32,-291.06"/>
|
|
||||||
<text text-anchor="middle" x="727.43" y="-312.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Vec<Vec<Value>>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- storage_error_node -->
|
<!-- storage_error_node -->
|
||||||
<g id="node8" class="node">
|
<g id="node8" class="node">
|
||||||
<title>storage_error_node</title>
|
<title>storage_error_node</title>
|
||||||
<path fill="#fbe9e7" stroke="#e64a19" stroke-width="1.5"
|
<path fill="#fbe9e7" stroke="#e64a19" stroke-width="1.5" d="M718.76,-1106C718.76,-1106 531.26,-1106 531.26,-1106 525.26,-1106 519.26,-1100 519.26,-1094 519.26,-1094 519.26,-878 519.26,-878 519.26,-872 525.26,-866 531.26,-866 531.26,-866 718.76,-866 718.76,-866 724.76,-866 730.76,-872 730.76,-878 730.76,-878 730.76,-1094 730.76,-1094 730.76,-1100 724.76,-1106 718.76,-1106"/>
|
||||||
d="M718.76,-1106C718.76,-1106 531.26,-1106 531.26,-1106 525.26,-1106 519.26,-1100 519.26,-1094 519.26,-1094 519.26,-878 519.26,-878 519.26,-872 525.26,-866 531.26,-866 531.26,-866 718.76,-866 718.76,-866 724.76,-866 730.76,-872 730.76,-878 730.76,-878 730.76,-1094 730.76,-1094 730.76,-1100 724.76,-1106 718.76,-1106"/>
|
<text text-anchor="start" x="557.51" y="-1085.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">StorageError</text>
|
||||||
<text text-anchor="start" x="557.51" y="-1085.7" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="641.51" y="-1085.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (enum)</text>
|
||||||
font-size="14.00">StorageError
|
<text text-anchor="start" x="531.26" y="-1055.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">RelationNotFound(String)</text>
|
||||||
</text>
|
<text text-anchor="start" x="531.26" y="-1026.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">RelationExists(String)</text>
|
||||||
<text text-anchor="start" x="641.51" y="-1085.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="531.26" y="-997.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">ArityMismatch { expected, got }</text>
|
||||||
 (enum)
|
<text text-anchor="start" x="531.26" y="-968.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Validation(String)</text>
|
||||||
</text>
|
<text text-anchor="start" x="531.26" y="-939.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Decode(CodecError)</text>
|
||||||
<text text-anchor="start" x="531.26" y="-1055.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="531.26" y="-910.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Unsupported(&'static str)</text>
|
||||||
RelationNotFound(String)
|
<text text-anchor="start" x="531.26" y="-881.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">Backend(Box<dyn Error>)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="531.26" y="-1026.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
RelationExists(String)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="531.26" y="-997.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
ArityMismatch { expected, got }
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="531.26" y="-968.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Validation(String)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="531.26" y="-939.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Decode(CodecError)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="531.26" y="-910.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Unsupported(&'static str)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="531.26" y="-881.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
Backend(Box<dyn Error>)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- codec_error_node -->
|
<!-- codec_error_node -->
|
||||||
<g id="node9" class="node">
|
<g id="node9" class="node">
|
||||||
<title>codec_error_node</title>
|
<title>codec_error_node</title>
|
||||||
<path fill="#fbe9e7" stroke="#e64a19" stroke-width="1.5"
|
<path fill="#fbe9e7" stroke="#e64a19" stroke-width="1.5" d="M736.76,-744.5C736.76,-744.5 513.26,-744.5 513.26,-744.5 507.26,-744.5 501.26,-738.5 501.26,-732.5 501.26,-732.5 501.26,-603.5 501.26,-603.5 501.26,-597.5 507.26,-591.5 513.26,-591.5 513.26,-591.5 736.76,-591.5 736.76,-591.5 742.76,-591.5 748.76,-597.5 748.76,-603.5 748.76,-603.5 748.76,-732.5 748.76,-732.5 748.76,-738.5 742.76,-744.5 736.76,-744.5"/>
|
||||||
d="M736.76,-744.5C736.76,-744.5 513.26,-744.5 513.26,-744.5 507.26,-744.5 501.26,-738.5 501.26,-732.5 501.26,-732.5 501.26,-603.5 501.26,-603.5 501.26,-597.5 507.26,-591.5 513.26,-591.5 513.26,-591.5 736.76,-591.5 736.76,-591.5 742.76,-591.5 748.76,-597.5 748.76,-603.5 748.76,-603.5 748.76,-732.5 748.76,-732.5 748.76,-738.5 742.76,-744.5 736.76,-744.5"/>
|
<text text-anchor="start" x="562.38" y="-724.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">CodecError</text>
|
||||||
<text text-anchor="start" x="562.38" y="-724.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="636.63" y="-724.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (enum)</text>
|
||||||
font-size="14.00">CodecError
|
<text text-anchor="start" x="513.26" y="-694.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">UnexpectedEof</text>
|
||||||
</text>
|
<text text-anchor="start" x="513.26" y="-665.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">UnknownTag(u8)</text>
|
||||||
<text text-anchor="start" x="636.63" y="-724.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="513.26" y="-636.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">LengthOverrun { declared, available }</text>
|
||||||
 (enum)
|
<text text-anchor="start" x="513.26" y="-607.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">InvalidUtf8</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="513.26" y="-694.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
UnexpectedEof
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="513.26" y="-665.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
UnknownTag(u8)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="513.26" y="-636.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
LengthOverrun { declared, available }
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="513.26" y="-607.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
InvalidUtf8
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- storage_error_node->codec_error_node -->
|
<!-- storage_error_node->codec_error_node -->
|
||||||
<g id="edge11" class="edge">
|
<g id="edge11" class="edge">
|
||||||
<title>storage_error_node->codec_error_node</title>
|
<title>storage_error_node->codec_error_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" d="M625.01,-865.53C625.01,-829.26 625.01,-790.11 625.01,-756.36"/>
|
||||||
d="M625.01,-865.53C625.01,-829.26 625.01,-790.11 625.01,-756.36"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="628.51,-756.48 625.01,-746.48 621.51,-756.48 628.51,-756.48"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="667.76" y="-832.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">Decode(CodecError)</text>
|
||||||
points="628.51,-756.48 625.01,-746.48 621.51,-756.48 628.51,-756.48"/>
|
|
||||||
<text text-anchor="middle" x="667.76" y="-832.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">Decode(CodecError)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- adapters_node -->
|
<!-- adapters_node -->
|
||||||
<g id="node10" class="node">
|
<g id="node10" class="node">
|
||||||
<title>adapters_node</title>
|
<title>adapters_node</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M418.38,-1091.5C418.38,-1091.5 201.63,-1091.5 201.63,-1091.5 195.63,-1091.5 189.63,-1085.5 189.63,-1079.5 189.63,-1079.5 189.63,-892.5 189.63,-892.5 189.63,-886.5 195.63,-880.5 201.63,-880.5 201.63,-880.5 418.38,-880.5 418.38,-880.5 424.38,-880.5 430.38,-886.5 430.38,-892.5 430.38,-892.5 430.38,-1079.5 430.38,-1079.5 430.38,-1085.5 424.38,-1091.5 418.38,-1091.5"/>
|
||||||
d="M418.38,-1091.5C418.38,-1091.5 201.63,-1091.5 201.63,-1091.5 195.63,-1091.5 189.63,-1085.5 189.63,-1079.5 189.63,-1079.5 189.63,-892.5 189.63,-892.5 189.63,-886.5 195.63,-880.5 201.63,-880.5 201.63,-880.5 418.38,-880.5 418.38,-880.5 424.38,-880.5 430.38,-886.5 430.38,-892.5 430.38,-892.5 430.38,-1079.5 430.38,-1079.5 430.38,-1085.5 424.38,-1091.5 418.38,-1091.5"/>
|
<text text-anchor="start" x="234.26" y="-1071.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Adapters</text>
|
||||||
<text text-anchor="start" x="234.26" y="-1071.2" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="292.76" y="-1071.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  (impl Storage)</text>
|
||||||
font-size="14.00">Adapters
|
<text text-anchor="start" x="201.63" y="-1041.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">MemoryStorage</text>
|
||||||
</text>
|
<text text-anchor="start" x="201.63" y="-1012.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">SqliteStorage  (feat sqlite)</text>
|
||||||
<text text-anchor="start" x="292.76" y="-1071.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="201.63" y="-983.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">RedbStorage  (feat redb)</text>
|
||||||
 (impl Storage)
|
<text text-anchor="start" x="201.63" y="-954.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">FjallStorage  (feat fjall)</text>
|
||||||
</text>
|
<text text-anchor="start" x="201.63" y="-925.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">LmdbStorage  (feat lmdb)</text>
|
||||||
<text text-anchor="start" x="201.63" y="-1041.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="start" x="201.63" y="-896.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">GeomergeStorage  (feat geomerge)</text>
|
||||||
MemoryStorage
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="201.63" y="-1012.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
SqliteStorage  (feat sqlite)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="201.63" y="-983.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
RedbStorage  (feat redb)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="201.63" y="-954.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
FjallStorage  (feat fjall)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="201.63" y="-925.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
LmdbStorage  (feat lmdb)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="201.63" y="-896.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
GeomergeStorage  (feat geomerge)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- adapters_node->storage_node -->
|
<!-- adapters_node->storage_node -->
|
||||||
<g id="edge1" class="edge">
|
<g id="edge1" class="edge">
|
||||||
<title>adapters_node->storage_node</title>
|
<title>adapters_node->storage_node</title>
|
||||||
<path fill="none" stroke="#333333" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#333333" stroke-width="1.2" stroke-dasharray="5,2" d="M310.01,-880.22C310.01,-859.1 310.01,-836.56 310.01,-814.34"/>
|
||||||
d="M310.01,-880.22C310.01,-859.1 310.01,-836.56 310.01,-814.34"/>
|
<polygon fill="#333333" stroke="#333333" stroke-width="1.2" points="313.51,-814.58 310.01,-804.58 306.51,-814.58 313.51,-814.58"/>
|
||||||
<polygon fill="#333333" stroke="#333333" stroke-width="1.2"
|
<text text-anchor="middle" x="318.63" y="-832.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">impl</text>
|
||||||
points="313.51,-814.58 310.01,-804.58 306.51,-814.58 313.51,-814.58"/>
|
|
||||||
<text text-anchor="middle" x="318.63" y="-832.45" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">impl
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 19 KiB |
@ -5,355 +5,222 @@
|
|||||||
-->
|
-->
|
||||||
<!-- Title: StorageWorkflow Pages: 1 -->
|
<!-- Title: StorageWorkflow Pages: 1 -->
|
||||||
<svg width="2196pt" height="573pt"
|
<svg width="2196pt" height="573pt"
|
||||||
viewBox="0.00 0.00 2195.75 573.00" xmlns="http://www.w3.org/2000/svg">
|
viewBox="0.00 0.00 2195.75 573.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 569)">
|
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 569)">
|
||||||
<title>StorageWorkflow</title>
|
<title>StorageWorkflow</title>
|
||||||
<polygon fill="white" stroke="none" points="-4,4 -4,-569 2191.75,-569 2191.75,4 -4,4"/>
|
<polygon fill="white" stroke="none" points="-4,4 -4,-569 2191.75,-569 2191.75,4 -4,4"/>
|
||||||
<g id="clust1" class="cluster">
|
<g id="clust1" class="cluster">
|
||||||
<title>cluster_inputs</title>
|
<title>cluster_inputs</title>
|
||||||
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2" points="23.5,-254 23.5,-557 211.75,-557 211.75,-254 23.5,-254"/>
|
||||||
points="23.5,-254 23.5,-557 211.75,-557 211.75,-254 23.5,-254"/>
|
<text text-anchor="middle" x="117.62" y="-539.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#555555">Inputs</text>
|
||||||
<text text-anchor="middle" x="117.62" y="-539.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#555555">Inputs
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust2" class="cluster">
|
<g id="clust2" class="cluster">
|
||||||
<title>cluster_setup</title>
|
<title>cluster_setup</title>
|
||||||
<polygon fill="white" stroke="#9c27b0" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#9c27b0" stroke-dasharray="5,2" points="8,-65 8,-246 524.75,-246 524.75,-65 8,-65"/>
|
||||||
points="8,-65 8,-246 524.75,-246 524.75,-65 8,-65"/>
|
<text text-anchor="middle" x="266.38" y="-228.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#7b1fa2">Setup  (open backend, declare relations)</text>
|
||||||
<text text-anchor="middle" x="266.38" y="-228.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#7b1fa2">Setup  (open backend, declare relations)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust3" class="cluster">
|
<g id="clust3" class="cluster">
|
||||||
<title>cluster_write</title>
|
<title>cluster_write</title>
|
||||||
<polygon fill="white" stroke="#4caf50" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#4caf50" stroke-dasharray="5,2" points="563.75,-323 563.75,-532 1882.5,-532 1882.5,-323 563.75,-323"/>
|
||||||
points="563.75,-323 563.75,-532 1882.5,-532 1882.5,-323 563.75,-323"/>
|
<text text-anchor="middle" x="1223.12" y="-514.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#388e3c">Write  (atomic batch via Transaction)</text>
|
||||||
<text text-anchor="middle" x="1223.12" y="-514.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#388e3c">Write  (atomic batch via Transaction)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust4" class="cluster">
|
<g id="clust4" class="cluster">
|
||||||
<title>cluster_read</title>
|
<title>cluster_read</title>
|
||||||
<polygon fill="white" stroke="#ff9800" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#ff9800" stroke-dasharray="5,2" points="1568.25,-8 1568.25,-315 1841.75,-315 1841.75,-8 1568.25,-8"/>
|
||||||
points="1568.25,-8 1568.25,-315 1841.75,-315 1841.75,-8 1568.25,-8"/>
|
<text text-anchor="middle" x="1705" y="-297.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#f57c00">Read</text>
|
||||||
<text text-anchor="middle" x="1705" y="-297.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#f57c00">Read
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<g id="clust5" class="cluster">
|
<g id="clust5" class="cluster">
|
||||||
<title>cluster_output</title>
|
<title>cluster_output</title>
|
||||||
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2"
|
<polygon fill="white" stroke="#888888" stroke-dasharray="5,2" points="1965.25,-117 1965.25,-306 2179.75,-306 2179.75,-117 1965.25,-117"/>
|
||||||
points="1965.25,-117 1965.25,-306 2179.75,-306 2179.75,-117 1965.25,-117"/>
|
<text text-anchor="middle" x="2072.5" y="-288.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00" fill="#555555">Output</text>
|
||||||
<text text-anchor="middle" x="2072.5" y="-288.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00"
|
|
||||||
fill="#555555">Output
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- schema -->
|
<!-- schema -->
|
||||||
<g id="node1" class="node">
|
<g id="node1" class="node">
|
||||||
<title>schema</title>
|
<title>schema</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M181.75,-366.12C181.75,-366.12 53.5,-366.12 53.5,-366.12 47.5,-366.12 41.5,-360.12 41.5,-354.12 41.5,-354.12 41.5,-283.88 41.5,-283.88 41.5,-277.88 47.5,-271.88 53.5,-271.88 53.5,-271.88 181.75,-271.88 181.75,-271.88 187.75,-271.88 193.75,-277.88 193.75,-283.88 193.75,-283.88 193.75,-354.12 193.75,-354.12 193.75,-360.12 187.75,-366.12 181.75,-366.12"/>
|
||||||
d="M181.75,-366.12C181.75,-366.12 53.5,-366.12 53.5,-366.12 47.5,-366.12 41.5,-360.12 41.5,-354.12 41.5,-354.12 41.5,-283.88 41.5,-283.88 41.5,-277.88 47.5,-271.88 53.5,-271.88 53.5,-271.88 181.75,-271.88 181.75,-271.88 187.75,-271.88 193.75,-277.88 193.75,-283.88 193.75,-283.88 193.75,-354.12 193.75,-354.12 193.75,-360.12 187.75,-366.12 181.75,-366.12"/>
|
<text text-anchor="start" x="91.38" y="-345.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Schema</text>
|
||||||
<text text-anchor="start" x="91.38" y="-345.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="53.5" y="-316.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• relation name</text>
|
||||||
font-size="14.00">Schema
|
<text text-anchor="start" x="53.5" y="-287.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• arity (column count)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="53.5" y="-316.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
relation name
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="53.5" y="-287.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
arity (column count)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- create_relation -->
|
<!-- create_relation -->
|
||||||
<g id="node4" class="node">
|
<g id="node4" class="node">
|
||||||
<title>create_relation</title>
|
<title>create_relation</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M498.75,-184C498.75,-184 292.25,-184 292.25,-184 286.25,-184 280.25,-178 280.25,-172 280.25,-172 280.25,-160 280.25,-160 280.25,-154 286.25,-148 292.25,-148 292.25,-148 498.75,-148 498.75,-148 504.75,-148 510.75,-154 510.75,-160 510.75,-160 510.75,-172 510.75,-172 510.75,-178 504.75,-184 498.75,-184"/>
|
||||||
d="M498.75,-184C498.75,-184 292.25,-184 292.25,-184 286.25,-184 280.25,-178 280.25,-172 280.25,-172 280.25,-160 280.25,-160 280.25,-154 286.25,-148 292.25,-148 292.25,-148 498.75,-148 498.75,-148 504.75,-148 510.75,-154 510.75,-160 510.75,-160 510.75,-172 510.75,-172 510.75,-178 504.75,-184 498.75,-184"/>
|
<text text-anchor="middle" x="395.5" y="-163.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">storage.create_relation(name, arity)</text>
|
||||||
<text text-anchor="middle" x="395.5" y="-163.2" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
storage.create_relation(name, arity)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- schema->create_relation -->
|
<!-- schema->create_relation -->
|
||||||
<g id="edge1" class="edge">
|
<g id="edge1" class="edge">
|
||||||
<title>schema->create_relation</title>
|
<title>schema->create_relation</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2"
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" d="M194.01,-277.19C244.45,-249.22 309.06,-213.38 351.1,-190.07"/>
|
||||||
d="M194.01,-277.19C244.45,-249.22 309.06,-213.38 351.1,-190.07"/>
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="352.64,-193.22 359.69,-185.31 349.24,-187.1 352.64,-193.22"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
|
||||||
points="352.64,-193.22 359.69,-185.31 349.24,-187.1 352.64,-193.22"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- row_data -->
|
<!-- row_data -->
|
||||||
<g id="node2" class="node">
|
<g id="node2" class="node">
|
||||||
<title>row_data</title>
|
<title>row_data</title>
|
||||||
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5"
|
<path fill="#e8f4fd" stroke="#2196f3" stroke-width="1.5" d="M178.75,-510.12C178.75,-510.12 56.5,-510.12 56.5,-510.12 50.5,-510.12 44.5,-504.12 44.5,-498.12 44.5,-498.12 44.5,-427.88 44.5,-427.88 44.5,-421.88 50.5,-415.88 56.5,-415.88 56.5,-415.88 178.75,-415.88 178.75,-415.88 184.75,-415.88 190.75,-421.88 190.75,-427.88 190.75,-427.88 190.75,-498.12 190.75,-498.12 190.75,-504.12 184.75,-510.12 178.75,-510.12"/>
|
||||||
d="M178.75,-510.12C178.75,-510.12 56.5,-510.12 56.5,-510.12 50.5,-510.12 44.5,-504.12 44.5,-498.12 44.5,-498.12 44.5,-427.88 44.5,-427.88 44.5,-421.88 50.5,-415.88 56.5,-415.88 56.5,-415.88 178.75,-415.88 178.75,-415.88 184.75,-415.88 190.75,-421.88 190.75,-427.88 190.75,-427.88 190.75,-498.12 190.75,-498.12 190.75,-504.12 184.75,-510.12 178.75,-510.12"/>
|
<text text-anchor="start" x="86.5" y="-489.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Row Data</text>
|
||||||
<text text-anchor="start" x="86.5" y="-489.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="56.5" y="-460.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• Vec<Value></text>
|
||||||
font-size="14.00">Row Data
|
<text text-anchor="start" x="56.5" y="-431.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• Int / Str / Id(RowId)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="56.5" y="-460.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
Vec<Value>
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="56.5" y="-431.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">•
|
|
||||||
Int / Str / Id(RowId)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- tx_ops -->
|
<!-- tx_ops -->
|
||||||
<g id="node6" class="node">
|
<g id="node6" class="node">
|
||||||
<title>tx_ops</title>
|
<title>tx_ops</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1020.75,-474.62C1020.75,-474.62 824.25,-474.62 824.25,-474.62 818.25,-474.62 812.25,-468.62 812.25,-462.62 812.25,-462.62 812.25,-363.38 812.25,-363.38 812.25,-357.38 818.25,-351.38 824.25,-351.38 824.25,-351.38 1020.75,-351.38 1020.75,-351.38 1026.75,-351.38 1032.75,-357.38 1032.75,-363.38 1032.75,-363.38 1032.75,-462.62 1032.75,-462.62 1032.75,-468.62 1026.75,-474.62 1020.75,-474.62"/>
|
||||||
d="M1020.75,-474.62C1020.75,-474.62 824.25,-474.62 824.25,-474.62 818.25,-474.62 812.25,-468.62 812.25,-462.62 812.25,-462.62 812.25,-363.38 812.25,-363.38 812.25,-357.38 818.25,-351.38 824.25,-351.38 824.25,-351.38 1020.75,-351.38 1020.75,-351.38 1026.75,-351.38 1032.75,-357.38 1032.75,-363.38 1032.75,-363.38 1032.75,-462.62 1032.75,-462.62 1032.75,-468.62 1026.75,-474.62 1020.75,-474.62"/>
|
<text text-anchor="start" x="862.88" y="-454.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">tx.insert / tx.delete</text>
|
||||||
<text text-anchor="start" x="862.88" y="-454.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="824.25" y="-425.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• insert yields pending RowId</text>
|
||||||
font-size="14.00">tx.insert / tx.delete
|
<text text-anchor="start" x="824.25" y="-396.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• pending RowIds reused as FKs</text>
|
||||||
</text>
|
<text text-anchor="start" x="824.25" y="-367.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• delete by RowId</text>
|
||||||
<text text-anchor="start" x="824.25" y="-425.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• insert yields pending RowId
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="824.25" y="-396.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• pending RowIds reused as FKs
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="824.25" y="-367.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• delete by RowId
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- row_data->tx_ops -->
|
<!-- row_data->tx_ops -->
|
||||||
<g id="edge5" class="edge">
|
<g id="edge5" class="edge">
|
||||||
<title>row_data->tx_ops</title>
|
<title>row_data->tx_ops</title>
|
||||||
<path fill="none" stroke="#2196f3" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#2196f3" stroke-width="1.2" stroke-dasharray="5,2" d="M190.94,-458.5C328.87,-449.91 630.86,-431.1 800.21,-420.55"/>
|
||||||
d="M190.94,-458.5C328.87,-449.91 630.86,-431.1 800.21,-420.55"/>
|
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2" points="800.34,-424.05 810.11,-419.94 799.91,-417.07 800.34,-424.05"/>
|
||||||
<polygon fill="#2196f3" stroke="#2196f3" stroke-width="1.2"
|
|
||||||
points="800.34,-424.05 810.11,-419.94 799.91,-417.07 800.34,-424.05"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- open_backend -->
|
<!-- open_backend -->
|
||||||
<g id="node3" class="node">
|
<g id="node3" class="node">
|
||||||
<title>open_backend</title>
|
<title>open_backend</title>
|
||||||
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5"
|
<path fill="#f3e5f5" stroke="#9c27b0" stroke-width="1.5" d="M201.25,-202.62C201.25,-202.62 34,-202.62 34,-202.62 28,-202.62 22,-196.62 22,-190.62 22,-190.62 22,-91.38 22,-91.38 22,-85.38 28,-79.38 34,-79.38 34,-79.38 201.25,-79.38 201.25,-79.38 207.25,-79.38 213.25,-85.38 213.25,-91.38 213.25,-91.38 213.25,-190.62 213.25,-190.62 213.25,-196.62 207.25,-202.62 201.25,-202.62"/>
|
||||||
d="M201.25,-202.62C201.25,-202.62 34,-202.62 34,-202.62 28,-202.62 22,-196.62 22,-190.62 22,-190.62 22,-91.38 22,-91.38 22,-85.38 28,-79.38 34,-79.38 34,-79.38 201.25,-79.38 201.25,-79.38 207.25,-79.38 213.25,-85.38 213.25,-91.38 213.25,-91.38 213.25,-190.62 213.25,-190.62 213.25,-196.62 207.25,-202.62 201.25,-202.62"/>
|
<text text-anchor="start" x="70" y="-182.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Open Backend</text>
|
||||||
<text text-anchor="start" x="70" y="-182.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="34" y="-153.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">MemoryStorage::new() /</text>
|
||||||
font-size="14.00">Open Backend
|
<text text-anchor="start" x="34" y="-124.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">SqliteStorage::open(path) /</text>
|
||||||
</text>
|
<text text-anchor="start" x="34" y="-95.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">FjallStorage::open(path) / ...</text>
|
||||||
<text text-anchor="start" x="34" y="-153.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
MemoryStorage::new() /
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="34" y="-124.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
SqliteStorage::open(path) /
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="34" y="-95.08" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
FjallStorage::open(path) / ...
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- open_backend->create_relation -->
|
<!-- open_backend->create_relation -->
|
||||||
<g id="edge2" class="edge">
|
<g id="edge2" class="edge">
|
||||||
<title>open_backend->create_relation</title>
|
<title>open_backend->create_relation</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" d="M213.57,-149.6C231.17,-151.2 249.8,-152.89 268.16,-154.55"/>
|
||||||
d="M213.57,-149.6C231.17,-151.2 249.8,-152.89 268.16,-154.55"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="267.78,-158.03 278.05,-155.45 268.41,-151.06 267.78,-158.03"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
|
||||||
points="267.78,-158.03 278.05,-155.45 268.41,-151.06 267.78,-158.03"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- begin_tx -->
|
<!-- begin_tx -->
|
||||||
<g id="node5" class="node">
|
<g id="node5" class="node">
|
||||||
<title>begin_tx</title>
|
<title>begin_tx</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M733.25,-387C733.25,-387 589.75,-387 589.75,-387 583.75,-387 577.75,-381 577.75,-375 577.75,-375 577.75,-349 577.75,-349 577.75,-343 583.75,-337 589.75,-337 589.75,-337 733.25,-337 733.25,-337 739.25,-337 745.25,-343 745.25,-349 745.25,-349 745.25,-375 745.25,-375 745.25,-381 739.25,-387 733.25,-387"/>
|
||||||
d="M733.25,-387C733.25,-387 589.75,-387 589.75,-387 583.75,-387 577.75,-381 577.75,-375 577.75,-375 577.75,-349 577.75,-349 577.75,-343 583.75,-337 589.75,-337 589.75,-337 733.25,-337 733.25,-337 739.25,-337 745.25,-343 745.25,-349 745.25,-349 745.25,-375 745.25,-375 745.25,-381 739.25,-387 733.25,-387"/>
|
<text text-anchor="middle" x="661.5" y="-369.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">storage.transaction()</text>
|
||||||
<text text-anchor="middle" x="661.5" y="-369.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="661.5" y="-348.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">-> Box<dyn Transaction></text>
|
||||||
storage.transaction()
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="661.5" y="-348.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
-> Box<dyn Transaction>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- create_relation->begin_tx -->
|
<!-- create_relation->begin_tx -->
|
||||||
<g id="edge3" class="edge">
|
<g id="edge3" class="edge">
|
||||||
<title>create_relation->begin_tx</title>
|
<title>create_relation->begin_tx</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M416.8,-184.23C444.61,-208.87 496.48,-253.65 543.75,-288 564.01,-302.72 587.16,-317.74 607.47,-330.37"/>
|
||||||
d="M416.8,-184.23C444.61,-208.87 496.48,-253.65 543.75,-288 564.01,-302.72 587.16,-317.74 607.47,-330.37"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="605.6,-333.32 615.94,-335.59 609.27,-327.37 605.6,-333.32"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="605.6,-333.32 615.94,-335.59 609.27,-327.37 605.6,-333.32"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- scan_iter -->
|
<!-- scan_iter -->
|
||||||
<g id="node9" class="node">
|
<g id="node9" class="node">
|
||||||
<title>scan_iter</title>
|
<title>scan_iter</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M1774.5,-272C1774.5,-272 1635.5,-272 1635.5,-272 1629.5,-272 1623.5,-266 1623.5,-260 1623.5,-260 1623.5,-234 1623.5,-234 1623.5,-228 1629.5,-222 1635.5,-222 1635.5,-222 1774.5,-222 1774.5,-222 1780.5,-222 1786.5,-228 1786.5,-234 1786.5,-234 1786.5,-260 1786.5,-260 1786.5,-266 1780.5,-272 1774.5,-272"/>
|
||||||
d="M1774.5,-272C1774.5,-272 1635.5,-272 1635.5,-272 1629.5,-272 1623.5,-266 1623.5,-260 1623.5,-260 1623.5,-234 1623.5,-234 1623.5,-228 1629.5,-222 1635.5,-222 1635.5,-222 1774.5,-222 1774.5,-222 1780.5,-222 1786.5,-228 1786.5,-234 1786.5,-234 1786.5,-260 1786.5,-260 1786.5,-266 1780.5,-272 1774.5,-272"/>
|
<text text-anchor="middle" x="1705" y="-254.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">storage.scan_iter(name)</text>
|
||||||
<text text-anchor="middle" x="1705" y="-254.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="1705" y="-233.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">-> RowStream</text>
|
||||||
storage.scan_iter(name)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="1705" y="-233.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
-> RowStream
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- create_relation->scan_iter -->
|
<!-- create_relation->scan_iter -->
|
||||||
<g id="edge8" class="edge">
|
<g id="edge8" class="edge">
|
||||||
<title>create_relation->scan_iter</title>
|
<title>create_relation->scan_iter</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" stroke-dasharray="5,2" d="M430.83,-184.43C480.17,-209.16 574.65,-250 660.5,-250 660.5,-250 660.5,-250 1261.62,-250 1382.54,-250 1522.03,-248.87 1611.53,-247.99"/>
|
||||||
d="M430.83,-184.43C480.17,-209.16 574.65,-250 660.5,-250 660.5,-250 660.5,-250 1261.62,-250 1382.54,-250 1522.03,-248.87 1611.53,-247.99"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="1611.5,-251.49 1621.46,-247.89 1611.43,-244.49 1611.5,-251.49"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
|
||||||
points="1611.5,-251.49 1621.46,-247.89 1611.43,-244.49 1611.5,-251.49"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- scan_where -->
|
<!-- scan_where -->
|
||||||
<g id="node10" class="node">
|
<g id="node10" class="node">
|
||||||
<title>scan_where</title>
|
<title>scan_where</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M1815.75,-72C1815.75,-72 1594.25,-72 1594.25,-72 1588.25,-72 1582.25,-66 1582.25,-60 1582.25,-60 1582.25,-34 1582.25,-34 1582.25,-28 1588.25,-22 1594.25,-22 1594.25,-22 1815.75,-22 1815.75,-22 1821.75,-22 1827.75,-28 1827.75,-34 1827.75,-34 1827.75,-60 1827.75,-60 1827.75,-66 1821.75,-72 1815.75,-72"/>
|
||||||
d="M1815.75,-72C1815.75,-72 1594.25,-72 1594.25,-72 1588.25,-72 1582.25,-66 1582.25,-60 1582.25,-60 1582.25,-34 1582.25,-34 1582.25,-28 1588.25,-22 1594.25,-22 1594.25,-22 1815.75,-22 1815.75,-22 1821.75,-22 1827.75,-28 1827.75,-34 1827.75,-34 1827.75,-60 1827.75,-60 1827.75,-66 1821.75,-72 1815.75,-72"/>
|
<text text-anchor="middle" x="1705" y="-54.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">storage.scan_where(name, col, value)</text>
|
||||||
<text text-anchor="middle" x="1705" y="-54.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="1705" y="-33.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">-> RowStream  (filtered)</text>
|
||||||
storage.scan_where(name, col, value)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="1705" y="-33.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
-> RowStream  (filtered)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- create_relation->scan_where -->
|
<!-- create_relation->scan_where -->
|
||||||
<g id="edge9" class="edge">
|
<g id="edge9" class="edge">
|
||||||
<title>create_relation->scan_where</title>
|
<title>create_relation->scan_where</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" stroke-dasharray="5,2" d="M419.28,-147.51C463.15,-113.85 563.58,-46 660.5,-46 660.5,-46 660.5,-46 1261.62,-46 1365.26,-46 1482.56,-46.28 1570.31,-46.54"/>
|
||||||
d="M419.28,-147.51C463.15,-113.85 563.58,-46 660.5,-46 660.5,-46 660.5,-46 1261.62,-46 1365.26,-46 1482.56,-46.28 1570.31,-46.54"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="1570.12,-50.04 1580.13,-46.57 1570.15,-43.04 1570.12,-50.04"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
|
||||||
points="1570.12,-50.04 1580.13,-46.57 1570.15,-43.04 1570.12,-50.04"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- scan_full -->
|
<!-- scan_full -->
|
||||||
<g id="node11" class="node">
|
<g id="node11" class="node">
|
||||||
<title>scan_full</title>
|
<title>scan_full</title>
|
||||||
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5"
|
<path fill="#fff3e0" stroke="#ff9800" stroke-width="1.5" d="M1792.5,-172C1792.5,-172 1617.5,-172 1617.5,-172 1611.5,-172 1605.5,-166 1605.5,-160 1605.5,-160 1605.5,-134 1605.5,-134 1605.5,-128 1611.5,-122 1617.5,-122 1617.5,-122 1792.5,-122 1792.5,-122 1798.5,-122 1804.5,-128 1804.5,-134 1804.5,-134 1804.5,-160 1804.5,-160 1804.5,-166 1798.5,-172 1792.5,-172"/>
|
||||||
d="M1792.5,-172C1792.5,-172 1617.5,-172 1617.5,-172 1611.5,-172 1605.5,-166 1605.5,-160 1605.5,-160 1605.5,-134 1605.5,-134 1605.5,-128 1611.5,-122 1617.5,-122 1617.5,-122 1792.5,-122 1792.5,-122 1798.5,-122 1804.5,-128 1804.5,-134 1804.5,-134 1804.5,-160 1804.5,-160 1804.5,-166 1798.5,-172 1792.5,-172"/>
|
<text text-anchor="middle" x="1705" y="-154.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">storage.scan(name)</text>
|
||||||
<text text-anchor="middle" x="1705" y="-154.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
<text text-anchor="middle" x="1705" y="-133.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">-> Vec<(RowId, Vec<Value>)></text>
|
||||||
storage.scan(name)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="middle" x="1705" y="-133.7" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
-> Vec<(RowId, Vec<Value>)>
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- create_relation->scan_full -->
|
<!-- create_relation->scan_full -->
|
||||||
<g id="edge10" class="edge">
|
<g id="edge10" class="edge">
|
||||||
<title>create_relation->scan_full</title>
|
<title>create_relation->scan_full</title>
|
||||||
<path fill="none" stroke="#9c27b0" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#9c27b0" stroke-width="1.2" stroke-dasharray="5,2" d="M511.06,-154.78C557.41,-151.13 611.46,-148 660.5,-148 660.5,-148 660.5,-148 1261.62,-148 1374.63,-148 1503.88,-147.67 1593.36,-147.39"/>
|
||||||
d="M511.06,-154.78C557.41,-151.13 611.46,-148 660.5,-148 660.5,-148 660.5,-148 1261.62,-148 1374.63,-148 1503.88,-147.67 1593.36,-147.39"/>
|
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2" points="1593.37,-150.89 1603.35,-147.36 1593.34,-143.89 1593.37,-150.89"/>
|
||||||
<polygon fill="#9c27b0" stroke="#9c27b0" stroke-width="1.2"
|
|
||||||
points="1593.37,-150.89 1603.35,-147.36 1593.34,-143.89 1593.37,-150.89"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- begin_tx->tx_ops -->
|
<!-- begin_tx->tx_ops -->
|
||||||
<g id="edge4" class="edge">
|
<g id="edge4" class="edge">
|
||||||
<title>begin_tx->tx_ops</title>
|
<title>begin_tx->tx_ops</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M745.52,-378.35C763.04,-381.8 781.87,-385.51 800.47,-389.17"/>
|
||||||
d="M745.52,-378.35C763.04,-381.8 781.87,-385.51 800.47,-389.17"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="799.73,-392.59 810.22,-391.09 801.09,-385.72 799.73,-392.59"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="799.73,-392.59 810.22,-391.09 801.09,-385.72 799.73,-392.59"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- commit -->
|
<!-- commit -->
|
||||||
<g id="node7" class="node">
|
<g id="node7" class="node">
|
||||||
<title>commit</title>
|
<title>commit</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1409.5,-489.12C1409.5,-489.12 1111.75,-489.12 1111.75,-489.12 1105.75,-489.12 1099.75,-483.12 1099.75,-477.12 1099.75,-477.12 1099.75,-348.88 1099.75,-348.88 1099.75,-342.88 1105.75,-336.88 1111.75,-336.88 1111.75,-336.88 1409.5,-336.88 1409.5,-336.88 1415.5,-336.88 1421.5,-342.88 1421.5,-348.88 1421.5,-348.88 1421.5,-477.12 1421.5,-477.12 1421.5,-483.12 1415.5,-489.12 1409.5,-489.12"/>
|
||||||
d="M1409.5,-489.12C1409.5,-489.12 1111.75,-489.12 1111.75,-489.12 1105.75,-489.12 1099.75,-483.12 1099.75,-477.12 1099.75,-477.12 1099.75,-348.88 1099.75,-348.88 1099.75,-342.88 1105.75,-336.88 1111.75,-336.88 1111.75,-336.88 1409.5,-336.88 1409.5,-336.88 1415.5,-336.88 1421.5,-342.88 1421.5,-348.88 1421.5,-348.88 1421.5,-477.12 1421.5,-477.12 1421.5,-483.12 1415.5,-489.12 1409.5,-489.12"/>
|
<text text-anchor="start" x="1223.5" y="-468.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">tx.commit()</text>
|
||||||
<text text-anchor="start" x="1223.5" y="-468.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="1111.75" y="-439.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• native commit (LMDB, redb, SQLite, geomerge)</text>
|
||||||
font-size="14.00">tx.commit()
|
<text text-anchor="start" x="1111.75" y="-410.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• buffered apply (memory, fjall)</text>
|
||||||
</text>
|
<text text-anchor="start" x="1111.75" y="-381.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• law validation (geomerge)</text>
|
||||||
<text text-anchor="start" x="1111.75" y="-439.57" font-family="Helvetica,Arial,sans-serif"
|
<text text-anchor="start" x="1111.75" y="-352.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• yields CommittedTx</text>
|
||||||
font-size="14.00">• native commit (LMDB, redb, SQLite, geomerge)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1111.75" y="-410.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• buffered apply (memory, fjall)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1111.75" y="-381.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• law validation (geomerge)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1111.75" y="-352.57" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• yields CommittedTx
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- tx_ops->commit -->
|
<!-- tx_ops->commit -->
|
||||||
<g id="edge6" class="edge">
|
<g id="edge6" class="edge">
|
||||||
<title>tx_ops->commit</title>
|
<title>tx_ops->commit</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M1033.09,-413C1050.7,-413 1069.29,-413 1087.94,-413"/>
|
||||||
d="M1033.09,-413C1050.7,-413 1069.29,-413 1087.94,-413"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1087.74,-416.5 1097.74,-413 1087.74,-409.5 1087.74,-416.5"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
|
||||||
points="1087.74,-416.5 1097.74,-413 1087.74,-409.5 1087.74,-416.5"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- resolve_ids -->
|
<!-- resolve_ids -->
|
||||||
<g id="node8" class="node">
|
<g id="node8" class="node">
|
||||||
<title>resolve_ids</title>
|
<title>resolve_ids</title>
|
||||||
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5"
|
<path fill="#e8f5e9" stroke="#4caf50" stroke-width="1.5" d="M1856.5,-438.12C1856.5,-438.12 1553.5,-438.12 1553.5,-438.12 1547.5,-438.12 1541.5,-432.12 1541.5,-426.12 1541.5,-426.12 1541.5,-355.88 1541.5,-355.88 1541.5,-349.88 1547.5,-343.88 1553.5,-343.88 1553.5,-343.88 1856.5,-343.88 1856.5,-343.88 1862.5,-343.88 1868.5,-349.88 1868.5,-355.88 1868.5,-355.88 1868.5,-426.12 1868.5,-426.12 1868.5,-432.12 1862.5,-438.12 1856.5,-438.12"/>
|
||||||
d="M1856.5,-438.12C1856.5,-438.12 1553.5,-438.12 1553.5,-438.12 1547.5,-438.12 1541.5,-432.12 1541.5,-426.12 1541.5,-426.12 1541.5,-355.88 1541.5,-355.88 1541.5,-349.88 1547.5,-343.88 1553.5,-343.88 1553.5,-343.88 1856.5,-343.88 1856.5,-343.88 1862.5,-343.88 1868.5,-349.88 1868.5,-355.88 1868.5,-355.88 1868.5,-426.12 1868.5,-426.12 1868.5,-432.12 1862.5,-438.12 1856.5,-438.12"/>
|
<text text-anchor="start" x="1633" y="-417.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">CommittedTx::resolve</text>
|
||||||
<text text-anchor="start" x="1633" y="-417.82" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="1553.5" y="-388.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• KV: pending == real</text>
|
||||||
font-size="14.00">CommittedTx::resolve
|
<text text-anchor="start" x="1553.5" y="-359.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• geomerge: pending counter → (commit, counter)</text>
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1553.5" y="-388.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• KV: pending == real
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1553.5" y="-359.57" font-family="Helvetica,Arial,sans-serif" font-size="14.00">
|
|
||||||
• geomerge: pending counter → (commit, counter)
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- commit->resolve_ids -->
|
<!-- commit->resolve_ids -->
|
||||||
<g id="edge7" class="edge">
|
<g id="edge7" class="edge">
|
||||||
<title>commit->resolve_ids</title>
|
<title>commit->resolve_ids</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" d="M1421.96,-405.03C1456.99,-403.28 1494.23,-401.43 1529.77,-399.66"/>
|
||||||
d="M1421.96,-405.03C1456.99,-403.28 1494.23,-401.43 1529.77,-399.66"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1529.61,-403.18 1539.43,-399.18 1529.27,-396.19 1529.61,-403.18"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
<text text-anchor="middle" x="1481.5" y="-408.27" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">CommittedTx</text>
|
||||||
points="1529.61,-403.18 1539.43,-399.18 1529.27,-396.19 1529.61,-403.18"/>
|
|
||||||
<text text-anchor="middle" x="1481.5" y="-408.27" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">CommittedTx
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- commit->scan_iter -->
|
<!-- commit->scan_iter -->
|
||||||
<g id="edge11" class="edge">
|
<g id="edge11" class="edge">
|
||||||
<title>commit->scan_iter</title>
|
<title>commit->scan_iter</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" stroke-dasharray="5,2" d="M1421.96,-352.84C1491.15,-326.87 1568.97,-297.67 1625.3,-276.53"/>
|
||||||
d="M1421.96,-352.84C1491.15,-326.87 1568.97,-297.67 1625.3,-276.53"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1626.44,-279.84 1634.58,-273.05 1623.98,-273.29 1626.44,-279.84"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
<text text-anchor="middle" x="1481.5" y="-344.89" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">after commit</text>
|
||||||
points="1626.44,-279.84 1634.58,-273.05 1623.98,-273.29 1626.44,-279.84"/>
|
|
||||||
<text text-anchor="middle" x="1481.5" y="-344.89" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">after commit
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- rows_out -->
|
<!-- rows_out -->
|
||||||
<g id="node12" class="node">
|
<g id="node12" class="node">
|
||||||
<title>rows_out</title>
|
<title>rows_out</title>
|
||||||
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5"
|
<path fill="#eceff1" stroke="#607d8b" stroke-width="1.5" d="M2149.75,-258.62C2149.75,-258.62 1995.25,-258.62 1995.25,-258.62 1989.25,-258.62 1983.25,-252.62 1983.25,-246.62 1983.25,-246.62 1983.25,-147.38 1983.25,-147.38 1983.25,-141.38 1989.25,-135.38 1995.25,-135.38 1995.25,-135.38 2149.75,-135.38 2149.75,-135.38 2155.75,-135.38 2161.75,-141.38 2161.75,-147.38 2161.75,-147.38 2161.75,-246.62 2161.75,-246.62 2161.75,-252.62 2155.75,-258.62 2149.75,-258.62"/>
|
||||||
d="M2149.75,-258.62C2149.75,-258.62 1995.25,-258.62 1995.25,-258.62 1989.25,-258.62 1983.25,-252.62 1983.25,-246.62 1983.25,-246.62 1983.25,-147.38 1983.25,-147.38 1983.25,-141.38 1989.25,-135.38 1995.25,-135.38 1995.25,-135.38 2149.75,-135.38 2149.75,-135.38 2155.75,-135.38 2161.75,-141.38 2161.75,-147.38 2161.75,-147.38 2161.75,-246.62 2161.75,-246.62 2161.75,-252.62 2155.75,-258.62 2149.75,-258.62"/>
|
<text text-anchor="start" x="2054.5" y="-238.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="14.00">Rows</text>
|
||||||
<text text-anchor="start" x="2054.5" y="-238.32" font-family="Helvetica,Arial,sans-serif" font-weight="bold"
|
<text text-anchor="start" x="1995.25" y="-209.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• (RowId, Vec<Value>)</text>
|
||||||
font-size="14.00">Rows
|
<text text-anchor="start" x="1995.25" y="-180.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">• consumed by query-ops</text>
|
||||||
</text>
|
<text text-anchor="start" x="1995.25" y="-151.07" font-family="Helvetica,Arial,sans-serif" font-size="14.00">  via scan_as_table</text>
|
||||||
<text text-anchor="start" x="1995.25" y="-209.07" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• (RowId, Vec<Value>)
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1995.25" y="-180.07" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00">• consumed by query-ops
|
|
||||||
</text>
|
|
||||||
<text text-anchor="start" x="1995.25" y="-151.07" font-family="Helvetica,Arial,sans-serif"
|
|
||||||
font-size="14.00"> via scan_as_table
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- resolve_ids->rows_out -->
|
<!-- resolve_ids->rows_out -->
|
||||||
<g id="edge15" class="edge">
|
<g id="edge15" class="edge">
|
||||||
<title>resolve_ids->rows_out</title>
|
<title>resolve_ids->rows_out</title>
|
||||||
<path fill="none" stroke="#4caf50" stroke-width="1.2" stroke-dasharray="5,2"
|
<path fill="none" stroke="#4caf50" stroke-width="1.2" stroke-dasharray="5,2" d="M1847.09,-343.54C1859.24,-338.34 1871.2,-332.82 1882.5,-327 1916.36,-309.58 1951.29,-287.04 1981.77,-265.63"/>
|
||||||
d="M1847.09,-343.54C1859.24,-338.34 1871.2,-332.82 1882.5,-327 1916.36,-309.58 1951.29,-287.04 1981.77,-265.63"/>
|
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2" points="1983.46,-268.72 1989.6,-260.08 1979.41,-263.01 1983.46,-268.72"/>
|
||||||
<polygon fill="#4caf50" stroke="#4caf50" stroke-width="1.2"
|
<text text-anchor="middle" x="1925.88" y="-320.11" font-family="Helvetica,Arial,sans-serif" font-size="9.00" fill="#555555">real RowIds</text>
|
||||||
points="1983.46,-268.72 1989.6,-260.08 1979.41,-263.01 1983.46,-268.72"/>
|
|
||||||
<text text-anchor="middle" x="1925.88" y="-320.11" font-family="Helvetica,Arial,sans-serif" font-size="9.00"
|
|
||||||
fill="#555555">real RowIds
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- scan_iter->rows_out -->
|
<!-- scan_iter->rows_out -->
|
||||||
<g id="edge12" class="edge">
|
<g id="edge12" class="edge">
|
||||||
<title>scan_iter->rows_out</title>
|
<title>scan_iter->rows_out</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M1786.66,-235.97C1841.16,-228.51 1913.48,-218.62 1971.62,-210.66"/>
|
||||||
d="M1786.66,-235.97C1841.16,-228.51 1913.48,-218.62 1971.62,-210.66"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="1971.81,-214.17 1981.25,-209.35 1970.86,-207.23 1971.81,-214.17"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
|
||||||
points="1971.81,-214.17 1981.25,-209.35 1970.86,-207.23 1971.81,-214.17"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- scan_where->rows_out -->
|
<!-- scan_where->rows_out -->
|
||||||
<g id="edge13" class="edge">
|
<g id="edge13" class="edge">
|
||||||
<title>scan_where->rows_out</title>
|
<title>scan_where->rows_out</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M1809.89,-72.49C1834.05,-79.5 1859.44,-87.75 1882.5,-97 1912.57,-109.06 1944.11,-124.52 1972.62,-139.66"/>
|
||||||
d="M1809.89,-72.49C1834.05,-79.5 1859.44,-87.75 1882.5,-97 1912.57,-109.06 1944.11,-124.52 1972.62,-139.66"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="1970.91,-142.71 1981.37,-144.36 1974.22,-136.55 1970.91,-142.71"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
|
||||||
points="1970.91,-142.71 1981.37,-144.36 1974.22,-136.55 1970.91,-142.71"/>
|
|
||||||
</g>
|
</g>
|
||||||
<!-- scan_full->rows_out -->
|
<!-- scan_full->rows_out -->
|
||||||
<g id="edge14" class="edge">
|
<g id="edge14" class="edge">
|
||||||
<title>scan_full->rows_out</title>
|
<title>scan_full->rows_out</title>
|
||||||
<path fill="none" stroke="#ff9800" stroke-width="1.2"
|
<path fill="none" stroke="#ff9800" stroke-width="1.2" d="M1804.91,-160.53C1856.5,-167.59 1919.48,-176.2 1971.27,-183.29"/>
|
||||||
d="M1804.91,-160.53C1856.5,-167.59 1919.48,-176.2 1971.27,-183.29"/>
|
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2" points="1970.63,-186.73 1981.02,-184.62 1971.58,-179.8 1970.63,-186.73"/>
|
||||||
<polygon fill="#ff9800" stroke="#ff9800" stroke-width="1.2"
|
|
||||||
points="1970.63,-186.73 1981.02,-184.62 1971.58,-179.8 1970.63,-186.73"/>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 19 KiB |
@ -16,7 +16,7 @@ use fjall::{Keyspace, PartitionCreateOptions, PartitionHandle};
|
|||||||
use crate::codec::{decode_meta, decode_row, encode_meta, encode_row};
|
use crate::codec::{decode_meta, decode_row, encode_meta, encode_row};
|
||||||
use crate::id::RowId;
|
use crate::id::RowId;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{CommittedTx, RowStream, Storage, StorageError, Transaction, backend};
|
use crate::{backend, CommittedTx, RowStream, Storage, StorageError, Transaction};
|
||||||
|
|
||||||
const META_PARTITION: &str = "__meta";
|
const META_PARTITION: &str = "__meta";
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ impl Transaction for FjallTx<'_> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{FjallStorage, backend};
|
use super::{backend, FjallStorage};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{Storage, StorageError};
|
use crate::{Storage, StorageError};
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@ use geomerge::txn::ops::{RowRef, TempRowId, TxnCellValue};
|
|||||||
|
|
||||||
use crate::id::RowId;
|
use crate::id::RowId;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{CommittedTx, RowStream, Storage, StorageError, Transaction, backend};
|
use crate::{backend, CommittedTx, RowStream, Storage, StorageError, Transaction};
|
||||||
|
|
||||||
const GM_ROW_ID_LEN: usize = 32 + 4;
|
const GM_ROW_ID_LEN: usize = 32 + 4;
|
||||||
const PENDING_ROW_ID_LEN: usize = 4;
|
const PENDING_ROW_ID_LEN: usize = 4;
|
||||||
@ -93,6 +93,17 @@ fn decode_pending_row_id(bytes: &[u8]) -> Result<TempRowId, StorageError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Geomerge-backed [`Storage`] implementation.
|
/// Geomerge-backed [`Storage`] implementation.
|
||||||
|
/// Primitive column type used by [`GeomergeStorage::with_relations`] to
|
||||||
|
/// synthesize a theory from an untyped `(name, arity)` schema. Geomerge
|
||||||
|
/// supports `PrimInt`, `PrimString`, and entity types; only the two
|
||||||
|
/// primitives are exposed here, since callers using this constructor by
|
||||||
|
/// definition don't carry entity-target information.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ColumnKind {
|
||||||
|
Int,
|
||||||
|
String,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GeomergeStorage {
|
pub struct GeomergeStorage {
|
||||||
store: Store,
|
store: Store,
|
||||||
declared: HashSet<String>,
|
declared: HashSet<String>,
|
||||||
@ -138,6 +149,52 @@ impl GeomergeStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a store with a theory synthesized from a flat list of
|
||||||
|
/// `(relation_name, column_kinds)`. Each `ColumnKind` is mapped to the
|
||||||
|
/// matching `PrimType`. No entity columns and no laws are declared.
|
||||||
|
///
|
||||||
|
/// This is the convenience constructor for callers (e.g., the
|
||||||
|
/// `plan-runner` CLI) whose schema only carries arity plus a column-by-
|
||||||
|
/// column primitive-type guess taken from a sample row. It exists so
|
||||||
|
/// those callers don't have to depend on `geolog-lang::ir` directly.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns [`StorageError::Backend`] if geomerge rejects the synthesized
|
||||||
|
/// theory.
|
||||||
|
pub fn with_relations<I, S>(relations: I) -> Result<Self, StorageError>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (S, Vec<ColumnKind>)>,
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
let tables: Vec<ir::TableEntry> = relations
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, kinds)| {
|
||||||
|
let columns = kinds
|
||||||
|
.into_iter()
|
||||||
|
.map(|k| ir::ColType::PrimType {
|
||||||
|
prim: match k {
|
||||||
|
ColumnKind::Int => ir::PrimType::PrimInt,
|
||||||
|
ColumnKind::String => ir::PrimType::PrimString,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let name: String = name.into();
|
||||||
|
ir::TableEntry {
|
||||||
|
path: name.into(),
|
||||||
|
table: ir::Schema {
|
||||||
|
columns,
|
||||||
|
primary_key: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let theory = ir::FlatTheory {
|
||||||
|
tables,
|
||||||
|
laws: Vec::new(),
|
||||||
|
};
|
||||||
|
Self::from_theory(theory)
|
||||||
|
}
|
||||||
|
|
||||||
/// Borrow the underlying geomerge store (for backend-specific operations
|
/// Borrow the underlying geomerge store (for backend-specific operations
|
||||||
/// like persistence, dump, or law inspection that aren't on the trait).
|
/// like persistence, dump, or law inspection that aren't on the trait).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use heed::{Database, Env, EnvOpenOptions, RwTxn};
|
|||||||
use crate::codec::{decode_meta, decode_row, encode_meta, encode_row, row_key};
|
use crate::codec::{decode_meta, decode_row, encode_meta, encode_row, row_key};
|
||||||
use crate::id::RowId;
|
use crate::id::RowId;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{CommittedTx, RowStream, Storage, StorageError, Transaction, backend};
|
use crate::{backend, CommittedTx, RowStream, Storage, StorageError, Transaction};
|
||||||
|
|
||||||
const META_DB: &str = "__meta";
|
const META_DB: &str = "__meta";
|
||||||
const DEFAULT_MAX_DBS: u32 = 128;
|
const DEFAULT_MAX_DBS: u32 = 128;
|
||||||
@ -154,12 +154,12 @@ impl Transaction for LmdbTx<'_> {
|
|||||||
let Some(wtxn) = self.wtxn.as_ref() else {
|
let Some(wtxn) = self.wtxn.as_ref() else {
|
||||||
unreachable!("transaction was already committed")
|
unreachable!("transaction was already committed")
|
||||||
};
|
};
|
||||||
let raw = self
|
let encoded = self
|
||||||
.meta
|
.meta
|
||||||
.get(wtxn, name.as_bytes())
|
.get(wtxn, name.as_bytes())
|
||||||
.map_err(backend)?
|
.map_err(backend)?
|
||||||
.ok_or_else(|| StorageError::RelationNotFound(name.to_string()))?;
|
.ok_or_else(|| StorageError::RelationNotFound(name.to_string()))?;
|
||||||
let entry = decode_meta(raw)?;
|
let entry = decode_meta(encoded)?;
|
||||||
self.next_ids.insert(name.to_string(), entry);
|
self.next_ids.insert(name.to_string(), entry);
|
||||||
entry
|
entry
|
||||||
};
|
};
|
||||||
@ -245,7 +245,7 @@ impl Transaction for LmdbTx<'_> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{LmdbStorage, backend};
|
use super::{backend, LmdbStorage};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{Storage, StorageError};
|
use crate::{Storage, StorageError};
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use redb::{Database, ReadableTable, TableDefinition, WriteTransaction};
|
|||||||
use crate::codec::{decode_meta, decode_row, encode_meta, encode_row};
|
use crate::codec::{decode_meta, decode_row, encode_meta, encode_row};
|
||||||
use crate::id::RowId;
|
use crate::id::RowId;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{CommittedTx, RowStream, Storage, StorageError, Transaction, backend};
|
use crate::{backend, CommittedTx, RowStream, Storage, StorageError, Transaction};
|
||||||
|
|
||||||
const META_TABLE: &str = "__meta";
|
const META_TABLE: &str = "__meta";
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ impl Transaction for RedbTx {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{RedbStorage, backend};
|
use super::{backend, RedbStorage};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{Storage, StorageError};
|
use crate::{Storage, StorageError};
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
//! SQLite adapter via the `rusqlite` crate (bundled libsqlite3).
|
//! `SQLite` adapter via the `rusqlite` crate (bundled libsqlite3).
|
||||||
//!
|
//!
|
||||||
//! Storage layout:
|
//! Storage layout:
|
||||||
//!
|
//!
|
||||||
@ -14,12 +14,12 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use rusqlite::{Connection, OptionalExtension, params};
|
use rusqlite::{params, Connection, OptionalExtension};
|
||||||
|
|
||||||
use crate::codec::{decode_row, encode_row};
|
use crate::codec::{decode_row, encode_row};
|
||||||
use crate::id::RowId;
|
use crate::id::RowId;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{CommittedTx, RowStream, Storage, StorageError, Transaction, backend};
|
use crate::{backend, CommittedTx, RowStream, Storage, StorageError, Transaction};
|
||||||
|
|
||||||
const SCHEMA_SQL: &str = "
|
const SCHEMA_SQL: &str = "
|
||||||
CREATE TABLE IF NOT EXISTS __meta (
|
CREATE TABLE IF NOT EXISTS __meta (
|
||||||
@ -35,13 +35,13 @@ CREATE TABLE IF NOT EXISTS __rows (
|
|||||||
);
|
);
|
||||||
";
|
";
|
||||||
|
|
||||||
/// SQLite-backed [`Storage`] implementation.
|
/// `SQLite`-backed [`Storage`] implementation.
|
||||||
pub struct SqliteStorage {
|
pub struct SqliteStorage {
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqliteStorage {
|
impl SqliteStorage {
|
||||||
/// Open or create a SQLite database at `path`. Pass `":memory:"` for
|
/// Open or create a `SQLite` database at `path`. Pass `":memory:"` for
|
||||||
/// an in-process database (useful in tests).
|
/// an in-process database (useful in tests).
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
-- against.
|
-- against.
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
glog-exporter.cabal
|
plan-exporter.cabal
|
||||||
../../external/geolog/geolog-lang/geolog-lang.cabal
|
../../external/geolog/geolog-lang/geolog-lang.cabal
|
||||||
../../external/geolog/data-partition/data-partition.cabal
|
../../external/geolog/data-partition/data-partition.cabal
|
||||||
../../external/geolog/diagnostician/diagnostician.cabal
|
../../external/geolog/diagnostician/diagnostician.cabal
|
||||||
|
|||||||
140
tools/exporter/examples/cartesian.scenario.json
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
{
|
||||||
|
"name": "cartesian",
|
||||||
|
"_description": "Two disjoint atoms over different tables. Exercises the 'stray' branch of planConjunction's spanning forest (no shared variables = no edge in the intersection graph) and the linear chain of natural-joins that fullJoinForest emits over disconnected components.",
|
||||||
|
"schema": {
|
||||||
|
"left": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "left"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "right"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"left": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"left",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"left",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"right": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"right",
|
||||||
|
10
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"right",
|
||||||
|
20
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"atoms": [
|
||||||
|
{
|
||||||
|
"table": "left",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "right",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"left",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"right",
|
||||||
|
10
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"left",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"right",
|
||||||
|
20
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"left",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"right",
|
||||||
|
10
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"left",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"right",
|
||||||
|
20
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
119
tools/exporter/examples/self_loop.scenario.json
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"name": "self-loop",
|
||||||
|
"_description": "Single-atom query with a repeated variable across two columns: edge(x, x, _). Exercises evalAtom's equality-enforcement path; the planner emits one PlanEvalAtom node and no joins.",
|
||||||
|
"schema": {
|
||||||
|
"edge": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "edge"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"edge": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"atoms": [
|
||||||
|
{
|
||||||
|
"table": "edge",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "x"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"var": "x"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"x"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
156
tools/exporter/examples/three_atom_chain.scenario.json
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
{
|
||||||
|
"name": "three-atom-chain",
|
||||||
|
"schema": {
|
||||||
|
"node": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"edge": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "edge"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"node": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"edge": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"atoms": [
|
||||||
|
{
|
||||||
|
"table": "node",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "edge",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "a"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"var": "b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "edge",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "b"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"var": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
144
tools/exporter/examples/two_atom_join.scenario.json
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
{
|
||||||
|
"name": "two-atom-join",
|
||||||
|
"schema": {
|
||||||
|
"node": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"edge": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": "edge"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"facts": {
|
||||||
|
"node": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"edge": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"edge",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"atoms": [
|
||||||
|
{
|
||||||
|
"table": "node",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "edge",
|
||||||
|
"values": {
|
||||||
|
"0": {
|
||||||
|
"var": "a"
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
"var": "b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expected_bindings": {
|
||||||
|
"columns": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
],
|
||||||
|
"rows": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity": [
|
||||||
|
"node",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,20 @@
|
|||||||
cabal-version: 3.4
|
cabal-version: 3.4
|
||||||
name: glog-exporter
|
name: plan-exporter
|
||||||
version: 0.1.0.0
|
version: 0.1.0.0
|
||||||
license: MIT OR Apache-2.0
|
license: MIT OR Apache-2.0
|
||||||
author: storage-engine-playground
|
author: storage-engine-playground
|
||||||
synopsis: Export geolog-lang join plans as JSON for the Rust runner.
|
synopsis: Export conjunctive-query plans as JSON for the Rust plan-runner.
|
||||||
description:
|
description:
|
||||||
Builds a FlatTheory + facts + a list of QAtoms for a named scenario,
|
Reads a scenario (FlatTheory + facts + a list of QAtoms) from JSON,
|
||||||
runs Geolog.DB.Plan.planConjunction, and emits a JSON document that
|
runs Geolog.DB.Plan.planConjunction, and emits a plan IR JSON document
|
||||||
crates/glog-runner consumes. This allows the playground use query-ops and
|
that crates/plan-runner consumes. The IR is the contract between the
|
||||||
storage end-to-end with a real Yannakakis plan produced by the geolog
|
Haskell frontend and the Rust executor; this tool is currently the only
|
||||||
frontend, not a hand-written fixture.
|
producer, but any frontend that emits the same JSON shape can drive the
|
||||||
|
runner.
|
||||||
|
|
||||||
build-type: Simple
|
build-type: Simple
|
||||||
|
|
||||||
executable glog-export
|
executable plan-export
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: GHC2024
|
default-language: GHC2024
|
||||||
@ -32,5 +33,6 @@ executable glog-export
|
|||||||
, base
|
, base
|
||||||
, bytestring
|
, bytestring
|
||||||
, containers
|
, containers
|
||||||
|
, fnotation
|
||||||
, geolog-lang
|
, geolog-lang
|
||||||
, text
|
, text
|
||||||
@ -1,31 +1,41 @@
|
|||||||
-- | Exports a geolog-lang join plan as JSON for the Rust runner in
|
-- | Reads a @.scenario.json@ example, plans its conjunction with
|
||||||
-- @crates/glog-runner@.
|
-- @Geolog.DB.Plan.planConjunction@, and writes a runner-IR JSON plan that
|
||||||
|
-- @crates\/plan-runner@ consumes.
|
||||||
--
|
--
|
||||||
-- Invocation:
|
-- Invocation:
|
||||||
--
|
--
|
||||||
-- @
|
-- @
|
||||||
-- cabal run glog-export -- <scenario> > plan.json
|
-- cabal run plan-export -- <scenario.json>
|
||||||
-- @
|
-- @
|
||||||
--
|
--
|
||||||
-- Available scenarios: @three-atom-chain@.
|
-- The scenario format is documented in @examples\/README@ or by example
|
||||||
|
-- (@examples\/*.scenario.json@); the output shape is documented in
|
||||||
|
-- @crates\/plan-runner\/src\/lib.rs@.
|
||||||
--
|
--
|
||||||
-- The output shape is documented in @crates\/glog-runner\/src\/lib.rs@.
|
-- The exporter is also a self-check: before emitting, it runs the planned
|
||||||
-- This program is the canonical producer: any change to the IR should
|
-- query through @evalConjunctionPlanned@ and verifies the bindings match
|
||||||
-- start here, with the Rust runner updated to match.
|
-- the scenario's @expected_bindings@. A mismatched scenario fails loudly
|
||||||
|
-- here rather than handing a bad fixture to the Rust runner.
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
import Algebra.Graph qualified as AG
|
import Algebra.Graph qualified as AG
|
||||||
import Data.Aeson ((.=))
|
import Control.Monad (unless)
|
||||||
|
import Data.Aeson ((.!=), (.:), (.:?), (.=))
|
||||||
import Data.Aeson qualified as Aeson
|
import Data.Aeson qualified as Aeson
|
||||||
import Data.Aeson.Encode.Pretty qualified as AesonPretty
|
import Data.Aeson.Encode.Pretty qualified as AesonPretty
|
||||||
import Data.Aeson.Key qualified as Key
|
import Data.Aeson.Key qualified as Key
|
||||||
|
import Data.Aeson.KeyMap qualified as KM
|
||||||
|
import Data.Aeson.Types (Parser)
|
||||||
import Data.ByteString.Lazy.Char8 qualified as LBS8
|
import Data.ByteString.Lazy.Char8 qualified as LBS8
|
||||||
|
import Data.Foldable (toList)
|
||||||
import Data.List (sortOn)
|
import Data.List (sortOn)
|
||||||
import Data.Map.Strict (Map)
|
import Data.Map.Strict (Map)
|
||||||
import Data.Map.Strict qualified as Map
|
import Data.Map.Strict qualified as Map
|
||||||
import Data.Set qualified as Set
|
import Data.Set qualified as Set
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
|
import Data.String (fromString)
|
||||||
|
import FNotation.Names (Name)
|
||||||
import Geolog.DB.InMemory
|
import Geolog.DB.InMemory
|
||||||
import Geolog.DB.Plan
|
import Geolog.DB.Plan
|
||||||
import Geolog.IR qualified as IR
|
import Geolog.IR qualified as IR
|
||||||
@ -33,74 +43,142 @@ import System.Environment (getArgs)
|
|||||||
import System.Exit (die)
|
import System.Exit (die)
|
||||||
import System.IO (hPutStrLn, stderr)
|
import System.IO (hPutStrLn, stderr)
|
||||||
|
|
||||||
-- * Scenario plumbing
|
-- * Scenario file format
|
||||||
--
|
--
|
||||||
-- A scenario fixes a schema, a set of ground facts, and a conjunction of
|
-- Mirrors @Geolog.IR.FlatTheory@ + @[(Path, [Val])]@ + @[QAtom]@. The
|
||||||
-- query atoms. The exporter is intentionally code-driven (not @.glog@
|
-- 'Expected' block is optional but, when present, the exporter cross-
|
||||||
-- driven): @.glog@ files declare theories, not queries, so the query
|
-- checks it against the planner's own evaluation before emitting.
|
||||||
-- side has to live in Haskell either way.
|
|
||||||
|
|
||||||
data Scenario = Scenario
|
data Scenario = Scenario
|
||||||
{ scName :: String
|
{ scName :: Text
|
||||||
, scTheory :: IR.FlatTheory
|
, scSchema :: Map IR.Path SchemaEntry
|
||||||
, scFacts :: [(IR.Path, [Val])]
|
, scFacts :: [(IR.Path, [Val])]
|
||||||
, scAtoms :: [QAtom]
|
, scAtoms :: [QAtom]
|
||||||
|
, scExpected :: Maybe Expected
|
||||||
}
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
-- * three-atom-chain
|
data SchemaEntry = SchemaEntry
|
||||||
--
|
{ seColumns :: [IR.ColType]
|
||||||
-- Mirrors @DB.InMemoryTest@ "matches evalConjunction on three-atom chain".
|
, sePrimaryKey :: Maybe [Int]
|
||||||
-- node = {e1, e2, e3}, edge = {(e1,e2,ee1), (e2,e3,ee2)}.
|
}
|
||||||
-- Conjunction: node(a), edge(a, b, _), edge(b, c, _).
|
deriving (Show)
|
||||||
|
|
||||||
nodePath, edgePath :: IR.Path
|
data Expected = Expected
|
||||||
nodePath = ["node"]
|
{ exColumns :: [Text]
|
||||||
edgePath = ["edge"]
|
, exRows :: [[Val]]
|
||||||
|
}
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
threeAtomChain :: Scenario
|
-- ** JSON parsers
|
||||||
threeAtomChain =
|
|
||||||
Scenario
|
parsePath :: Aeson.Value -> Parser IR.Path
|
||||||
{ scName = "three-atom-chain"
|
parsePath = Aeson.withText "path" \t -> pure [nameFromText t]
|
||||||
, scTheory =
|
|
||||||
IR.FlatTheory
|
-- | Build a single-segment 'Name' from text. Multi-segment names (which
|
||||||
{ tables =
|
-- would carry a non-empty 'init' field) aren't needed by any current
|
||||||
|
-- example; if a scenario wants @"a/b"@-style paths, extend this helper.
|
||||||
|
nameFromText :: Text -> Name
|
||||||
|
nameFromText = fromString . T.unpack
|
||||||
|
|
||||||
|
instance Aeson.FromJSON SchemaEntry where
|
||||||
|
parseJSON = Aeson.withObject "SchemaEntry" \o ->
|
||||||
|
SchemaEntry <$> o .: "columns" <*> o .:? "primaryKey"
|
||||||
|
|
||||||
|
instance Aeson.FromJSON IR.ColType where
|
||||||
|
parseJSON = Aeson.withObject "ColType" \o -> do
|
||||||
|
case KM.toList o of
|
||||||
|
[("entity", v)] -> IR.EntityType <$> parsePath v
|
||||||
|
[("prim", v)] -> IR.PrimType <$> parsePrim v
|
||||||
|
_ -> fail "ColType: expected {\"entity\": <path>} or {\"prim\": \"int\"|\"string\"}"
|
||||||
|
|
||||||
|
parsePrim :: Aeson.Value -> Parser IR.PrimType
|
||||||
|
parsePrim = Aeson.withText "prim type" \case
|
||||||
|
"int" -> pure IR.PrimInt
|
||||||
|
"string" -> pure IR.PrimString
|
||||||
|
other -> fail ("unknown primitive type: " <> T.unpack other)
|
||||||
|
|
||||||
|
parseVal :: Aeson.Value -> Parser Val
|
||||||
|
parseVal = Aeson.withObject "Val" \o ->
|
||||||
|
case KM.toList o of
|
||||||
|
[("int", v)] -> ValInt <$> Aeson.parseJSON v
|
||||||
|
[("str", v)] -> ValText <$> Aeson.parseJSON v
|
||||||
|
[("entity", v)] -> parseEntity v
|
||||||
|
_ -> fail "Val: expected {\"int\": ..} | {\"str\": ..} | {\"entity\": [<path>, <id>]}"
|
||||||
|
where
|
||||||
|
parseEntity = Aeson.withArray "entity" \arr -> case toList arr of
|
||||||
|
[pv, nv] -> do
|
||||||
|
p <- parsePath pv
|
||||||
|
n <- Aeson.parseJSON nv
|
||||||
|
pure (ValEntity p n)
|
||||||
|
_ -> fail "entity: expected [<path>, <id>]"
|
||||||
|
|
||||||
|
parseQVal :: Aeson.Value -> Parser QVal
|
||||||
|
parseQVal = Aeson.withObject "QVal" \o ->
|
||||||
|
case KM.toList o of
|
||||||
|
[("var", v)] -> QVar . Var <$> Aeson.parseJSON v
|
||||||
|
[("lit", v)] -> QLit <$> parseVal v
|
||||||
|
_ -> fail "QVal: expected {\"var\": \"name\"} or {\"lit\": <value>}"
|
||||||
|
|
||||||
|
parseAtom :: Aeson.Value -> Parser QAtom
|
||||||
|
parseAtom = Aeson.withObject "QAtom" \o -> do
|
||||||
|
qaTable <- o .: "table" >>= parsePath
|
||||||
|
qaRowId <- o .:? "rowId" >>= traverse parseQVal
|
||||||
|
values <- o .: "values" :: Parser (Map Text Aeson.Value)
|
||||||
|
qaValues <-
|
||||||
Map.fromList
|
Map.fromList
|
||||||
[ (nodePath, IR.Table {columns = [IR.EntityType nodePath], primaryKey = Nothing})
|
<$> traverse
|
||||||
, (edgePath, IR.Table {columns = [IR.EntityType nodePath, IR.EntityType nodePath, IR.EntityType edgePath], primaryKey = Nothing})
|
( \(k, v) -> case reads (T.unpack k) of
|
||||||
]
|
[(i, "")] -> (i,) <$> parseQVal v
|
||||||
|
_ -> fail ("non-integer key in atom values: " <> T.unpack k)
|
||||||
|
)
|
||||||
|
(Map.toList values)
|
||||||
|
pure QAtom {qaTable, qaRowId, qaValues}
|
||||||
|
|
||||||
|
parseExpected :: Aeson.Value -> Parser Expected
|
||||||
|
parseExpected = Aeson.withObject "Expected" \o -> do
|
||||||
|
exColumns <- o .: "columns"
|
||||||
|
rawRows <- o .: "rows" :: Parser [[Aeson.Value]]
|
||||||
|
exRows <- traverse (traverse parseVal) rawRows
|
||||||
|
pure Expected {exColumns, exRows}
|
||||||
|
|
||||||
|
instance Aeson.FromJSON Scenario where
|
||||||
|
parseJSON = Aeson.withObject "Scenario" \o -> do
|
||||||
|
scName <- o .:? "name" .!= "unnamed"
|
||||||
|
rawSchema <- o .: "schema" :: Parser (Map Text SchemaEntry)
|
||||||
|
let scSchema = Map.fromList [([nameFromText k], v) | (k, v) <- Map.toList rawSchema]
|
||||||
|
rawFacts <- o .:? "facts" .!= mempty :: Parser (Map Text [[Aeson.Value]])
|
||||||
|
scFacts <-
|
||||||
|
concat
|
||||||
|
<$> traverse
|
||||||
|
( \(name, rows) -> do
|
||||||
|
let path = [nameFromText name]
|
||||||
|
parsedRows <- traverse (traverse parseVal) rows
|
||||||
|
pure [(path, row) | row <- parsedRows]
|
||||||
|
)
|
||||||
|
(Map.toList rawFacts)
|
||||||
|
rawAtoms <- o .: "atoms" :: Parser [Aeson.Value]
|
||||||
|
scAtoms <- traverse parseAtom rawAtoms
|
||||||
|
scExpected <- o .:? "expected_bindings" >>= traverse parseExpected
|
||||||
|
pure Scenario {scName, scSchema, scFacts, scAtoms, scExpected}
|
||||||
|
|
||||||
|
-- * Scenario → FlatTheory + DB + atoms
|
||||||
|
|
||||||
|
toFlatTheory :: Scenario -> IR.FlatTheory
|
||||||
|
toFlatTheory sc =
|
||||||
|
IR.FlatTheory
|
||||||
|
{ tables = Map.map (\e -> IR.Table {columns = seColumns e, primaryKey = sePrimaryKey e}) sc.scSchema
|
||||||
, laws = Map.empty
|
, laws = Map.empty
|
||||||
}
|
}
|
||||||
, scFacts =
|
|
||||||
[ (nodePath, [ValEntity nodePath 1])
|
|
||||||
, (nodePath, [ValEntity nodePath 2])
|
|
||||||
, (nodePath, [ValEntity nodePath 3])
|
|
||||||
, (edgePath, [ValEntity nodePath 1, ValEntity nodePath 2, ValEntity edgePath 1])
|
|
||||||
, (edgePath, [ValEntity nodePath 2, ValEntity nodePath 3, ValEntity edgePath 2])
|
|
||||||
]
|
|
||||||
, scAtoms =
|
|
||||||
[ QAtom {qaTable = nodePath, qaRowId = Nothing, qaValues = Map.singleton 0 (QVar (Var "a"))}
|
|
||||||
, QAtom {qaTable = edgePath, qaRowId = Nothing, qaValues = Map.fromList [(0, QVar (Var "a")), (1, QVar (Var "b"))]}
|
|
||||||
, QAtom {qaTable = edgePath, qaRowId = Nothing, qaValues = Map.fromList [(0, QVar (Var "b")), (1, QVar (Var "c"))]}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios :: [Scenario]
|
populateDB :: Scenario -> DB
|
||||||
scenarios = [threeAtomChain]
|
populateDB sc = foldl (\d (p, row) -> insertRow p row d) (fromTheory (toFlatTheory sc)) sc.scFacts
|
||||||
|
|
||||||
-- * JSON encoding
|
-- * JSON encoding for the plan-runner IR
|
||||||
--
|
--
|
||||||
-- The shape mirrors the IR in @crates/glog-runner/src/lib.rs@:
|
-- The shape is the same one we settled on earlier; see
|
||||||
--
|
-- @crates/plan-runner/src/lib.rs@.
|
||||||
-- > {
|
|
||||||
-- > "schema": {<name>: <arity>, ...},
|
|
||||||
-- > "facts": {<name>: [[<value>, ...], ...], ...},
|
|
||||||
-- > "query": {"root": <id>, "nodes": [{"id": <id>, "action": <action>}, ...]}
|
|
||||||
-- > }
|
|
||||||
|
|
||||||
-- | Render a 'Geolog.IR.Path' (a list of 'FNotation.Names.Name') as a flat
|
|
||||||
-- string for use as a relation name on the Rust side. Each 'Name' is
|
|
||||||
-- already shown with @\/@ between its own init segments and last, so we
|
|
||||||
-- reuse 'show' and join Names with @\/@ too.
|
|
||||||
pathText :: IR.Path -> Text
|
pathText :: IR.Path -> Text
|
||||||
pathText = T.intercalate "/" . map (T.pack . show)
|
pathText = T.intercalate "/" . map (T.pack . show)
|
||||||
|
|
||||||
@ -119,10 +197,6 @@ encodeTerm = \case
|
|||||||
QVar (Var name) -> Aeson.object ["var" .= name]
|
QVar (Var name) -> Aeson.object ["var" .= name]
|
||||||
QLit v -> Aeson.object ["lit" .= encodeValue v]
|
QLit v -> Aeson.object ["lit" .= encodeValue v]
|
||||||
|
|
||||||
-- | Flatten an atom into one term per stored column, mirroring
|
|
||||||
-- @Geolog.DB.InMemory.toFlatArgs@: @qaValues@ keys map to positions
|
|
||||||
-- @0..n-2@, @qaRowId@ (if present) maps to position @n-1@, and any
|
|
||||||
-- missing positions become wildcard variables with locally-unique names.
|
|
||||||
flattenAtom :: Int -> Int -> QAtom -> [Aeson.Value]
|
flattenAtom :: Int -> Int -> QAtom -> [Aeson.Value]
|
||||||
flattenAtom atomIdx arity qa =
|
flattenAtom atomIdx arity qa =
|
||||||
[ encodeTerm (Map.findWithDefault (wildcard atomIdx pos) pos merged)
|
[ encodeTerm (Map.findWithDefault (wildcard atomIdx pos) pos merged)
|
||||||
@ -145,9 +219,6 @@ encodeAtom tables atomIdx qa =
|
|||||||
Just t -> length t.columns
|
Just t -> length t.columns
|
||||||
Nothing -> error ("encodeAtom: unknown table " <> show qa.qaTable)
|
Nothing -> error ("encodeAtom: unknown table " <> show qa.qaTable)
|
||||||
|
|
||||||
-- | Stable atom indexing keyed by atom identity, so the wildcard names in
|
|
||||||
-- @flattenAtom@ are deterministic across runs even if the planner's node
|
|
||||||
-- ordering changes.
|
|
||||||
atomIndex :: [QAtom] -> Map QAtom Int
|
atomIndex :: [QAtom] -> Map QAtom Int
|
||||||
atomIndex atoms = Map.fromList (zip (Set.toList (Set.fromList atoms)) [0 ..])
|
atomIndex atoms = Map.fromList (zip (Set.toList (Set.fromList atoms)) [0 ..])
|
||||||
|
|
||||||
@ -176,9 +247,6 @@ encodeNode tables idx n =
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | Render a 'PlanGraph' as the JSON the runner consumes. Empty graphs
|
|
||||||
-- produce @{"root": 0, "nodes": []}@, which the runner treats as a
|
|
||||||
-- well-formed but empty query.
|
|
||||||
encodeQuery :: Map IR.Path IR.Table -> Map QAtom Int -> PlanGraph -> Aeson.Value
|
encodeQuery :: Map IR.Path IR.Table -> Map QAtom Int -> PlanGraph -> Aeson.Value
|
||||||
encodeQuery tables idx (PlanGraph g)
|
encodeQuery tables idx (PlanGraph g)
|
||||||
| null nodes =
|
| null nodes =
|
||||||
@ -192,24 +260,30 @@ encodeQuery tables idx (PlanGraph g)
|
|||||||
nodes = sortOn (.graphId.unPlanNodeId) (AG.vertexList g)
|
nodes = sortOn (.graphId.unPlanNodeId) (AG.vertexList g)
|
||||||
rootId = case graphRoot (PlanGraph g) of
|
rootId = case graphRoot (PlanGraph g) of
|
||||||
Just (PlanNodeId i) -> i
|
Just (PlanNodeId i) -> i
|
||||||
-- Non-empty graph with no topological root means a cycle, which
|
|
||||||
-- planConjunction never produces. Fall back to the last id rather
|
|
||||||
-- than crashing so a bug here is still inspectable.
|
|
||||||
Nothing -> (.graphId.unPlanNodeId) (last nodes)
|
Nothing -> (.graphId.unPlanNodeId) (last nodes)
|
||||||
|
|
||||||
|
encodeExpected :: Expected -> Aeson.Value
|
||||||
|
encodeExpected ex =
|
||||||
|
Aeson.object
|
||||||
|
[ "columns" .= exColumns ex
|
||||||
|
, "rows" .= map (map encodeValue) (exRows ex)
|
||||||
|
]
|
||||||
|
|
||||||
encodePlan :: Scenario -> Aeson.Value
|
encodePlan :: Scenario -> Aeson.Value
|
||||||
encodePlan sc =
|
encodePlan sc =
|
||||||
Aeson.object
|
Aeson.object
|
||||||
[ "_scenario" .= sc.scName
|
( [ "_scenario" .= scName sc
|
||||||
, "schema" .= Aeson.object
|
, "schema" .= Aeson.object [pathKey p .= length (seColumns t) | (p, t) <- Map.toList sc.scSchema]
|
||||||
[pathKey p .= length t.columns | (p, t) <- Map.toList sc.scTheory.tables]
|
, "facts"
|
||||||
, "facts" .= Aeson.object
|
.= Aeson.object
|
||||||
[pathKey p .= map (map encodeValue) rows | (p, rows) <- groupedFacts sc.scFacts]
|
[ pathKey p .= map (map encodeValue) rows
|
||||||
, "query" .= encodeQuery sc.scTheory.tables (atomIndex sc.scAtoms) (planConjunction sc.scAtoms)
|
| (p, rows) <- groupedFacts sc.scFacts
|
||||||
]
|
]
|
||||||
|
, "query" .= encodeQuery (toFlatTheory sc).tables (atomIndex sc.scAtoms) (planConjunction sc.scAtoms)
|
||||||
|
]
|
||||||
|
++ maybe [] (\e -> ["expected_bindings" .= encodeExpected e]) sc.scExpected
|
||||||
|
)
|
||||||
|
|
||||||
-- | Group facts by table while preserving table-first-seen order and
|
|
||||||
-- per-table insertion order.
|
|
||||||
groupedFacts :: [(IR.Path, [Val])] -> [(IR.Path, [[Val]])]
|
groupedFacts :: [(IR.Path, [Val])] -> [(IR.Path, [[Val]])]
|
||||||
groupedFacts = go []
|
groupedFacts = go []
|
||||||
where
|
where
|
||||||
@ -222,17 +296,45 @@ groupedFacts = go []
|
|||||||
|
|
||||||
-- * Self-check
|
-- * Self-check
|
||||||
--
|
--
|
||||||
-- Run the planner's @evalConjunctionPlanned@ against the scenario's DB
|
-- Cross-check the planned bindings against any user-supplied
|
||||||
-- to confirm the plan we're about to emit is well-formed and produces
|
-- 'expected_bindings'. Detects two classes of bug before they reach the
|
||||||
-- non-error output. Catches malformed scenarios before they hand a bad
|
-- Rust side: a scenario whose 'expected' is wrong, and a planner output
|
||||||
-- plan to the Rust runner.
|
-- that disagrees with 'evalConjunction'.
|
||||||
|
|
||||||
selfCheck :: Scenario -> IO ()
|
selfCheck :: Scenario -> IO ()
|
||||||
selfCheck sc = do
|
selfCheck sc = do
|
||||||
let db = foldl (\d (p, row) -> insertRow p row d) (fromTheory sc.scTheory) sc.scFacts
|
let db = populateDB sc
|
||||||
case evalConjunctionPlanned db sc.scAtoms of
|
case evalConjunctionPlanned db sc.scAtoms of
|
||||||
Left err -> die ("self-check failed for " <> sc.scName <> ": " <> show err)
|
Left err -> die ("self-check failed for " <> T.unpack sc.scName <> ": " <> show err)
|
||||||
Right _ -> pure ()
|
Right actual -> case sc.scExpected of
|
||||||
|
Nothing -> pure ()
|
||||||
|
Just expected -> verifyAgainstExpected sc.scName expected actual
|
||||||
|
|
||||||
|
verifyAgainstExpected :: Text -> Expected -> Bindings -> IO ()
|
||||||
|
verifyAgainstExpected name expected actual = do
|
||||||
|
let actualCols = actual.cols
|
||||||
|
expectedCols = Set.fromList (map Var (exColumns expected))
|
||||||
|
unless (Set.isSubsetOf expectedCols actualCols) $
|
||||||
|
die $
|
||||||
|
"self-check failed for "
|
||||||
|
<> T.unpack name
|
||||||
|
<> ": expected_bindings names columns not produced by the plan: "
|
||||||
|
<> show (Set.difference expectedCols actualCols)
|
||||||
|
let projectedActual = Set.map (`projectOn` exColumns expected) actual.table
|
||||||
|
expectedProjected = Set.fromList (map (zip (exColumns expected)) (exRows expected))
|
||||||
|
expectedSet = Set.map (Map.fromList . map (\(v, x) -> (Var v, x))) expectedProjected
|
||||||
|
unless (projectedActual == expectedSet) $
|
||||||
|
die $
|
||||||
|
"self-check failed for "
|
||||||
|
<> T.unpack name
|
||||||
|
<> ":\n expected: "
|
||||||
|
<> show expectedSet
|
||||||
|
<> "\n actual: "
|
||||||
|
<> show projectedActual
|
||||||
|
|
||||||
|
projectOn :: Map Var Val -> [Text] -> Map Var Val
|
||||||
|
projectOn row keys =
|
||||||
|
Map.fromList [(Var k, v) | k <- keys, Just v <- [Map.lookup (Var k) row]]
|
||||||
|
|
||||||
-- * Entry point
|
-- * Entry point
|
||||||
|
|
||||||
@ -240,13 +342,13 @@ main :: IO ()
|
|||||||
main = do
|
main = do
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
case args of
|
case args of
|
||||||
[name] -> case lookup name [(s.scName, s) | s <- scenarios] of
|
[path] -> do
|
||||||
Just sc -> do
|
raw <- LBS8.readFile path
|
||||||
|
sc <- case Aeson.eitherDecode raw of
|
||||||
|
Left err -> die ("failed to parse " <> path <> ": " <> err)
|
||||||
|
Right sc -> pure sc
|
||||||
selfCheck sc
|
selfCheck sc
|
||||||
LBS8.putStrLn (AesonPretty.encodePretty (encodePlan sc))
|
LBS8.putStrLn (AesonPretty.encodePretty (encodePlan sc))
|
||||||
Nothing ->
|
|
||||||
die ("unknown scenario: " <> name <> "\navailable: " <> unwords (map (.scName) scenarios))
|
|
||||||
_ -> do
|
_ -> do
|
||||||
hPutStrLn stderr "usage: glog-export <scenario>"
|
hPutStrLn stderr "usage: plan-export <scenario.json>"
|
||||||
hPutStrLn stderr ("scenarios: " <> unwords (map (.scName) scenarios))
|
|
||||||
die ""
|
die ""
|
||||||
|
|||||||