Query Engine
An experimental Rust project for building query-engine components.
Right now the repository is centered on a chase-based reasoning core, a small interactive frontend, and an early relational/SQL scaffold. The broader target shape is a query engine with clearer front-end, planning, optimization, and execution boundaries.
Current scope
- Chase-based rule evaluation over facts, rules, and substitutions
- Restricted-chase style materialization with active-trigger checks
- Provenance-oriented explanations for derived answers
- Script, REPL, and local web UI for experimentation
- Relational schema, catalog, logical-plan, and execution scaffolding
- A minimal SQL slice for
SELECT-FROM-WHEREqueries over predicate-backed tables
Architecture
The repository is currently organized around a few clear subsystems:
src/chase/: rule-engine data structures and chase executionsrc/frontend/: REPL, script, GUI, and explanation renderingsrc/relational/: schemas, values, rows, and result setssrc/catalog/: predicate-backed table metadatasrc/sql/: minimal SQL AST and parsersrc/planner/: logical plan structures and SQL-to-plan translationsrc/execution/: execution for the current logical-plan subset
Today, the chase subsystem is still the most mature part of the codebase. The relational and SQL modules are present to create clean extension points for a broader query-engine architecture.
Intended Direction
The medium-term direction is to evolve this project into a more general query-engine playground with:
- explicit front-end and parsing layers
- internal planning representations
- clearer separation between logical meaning and execution strategy
- support for multiple query-engine experiments instead of only chase logic
The current code now includes an initial SQL front end, logical plan, and execution path. It is still intentionally narrow and should not be read as full SQL support.
Quickstart
Rust API
use query_engine::{Atom, Instance, Term, chase};
use query_engine::chase::rule::RuleBuilder;
let instance: Instance = vec![
Atom::new("Parent", vec![Term::constant("alice"), Term::constant("bob")]),
Atom::new("Parent", vec![Term::constant("bob"), Term::constant("carol")]),
]
.into_iter()
.collect();
let rule1 = RuleBuilder::new()
.when("Parent", vec![Term::var("X"), Term::var("Y")])
.then("Ancestor", vec![Term::var("X"), Term::var("Y")])
.build();
let rule2 = RuleBuilder::new()
.when("Ancestor", vec![Term::var("X"), Term::var("Y")])
.when("Parent", vec![Term::var("Y"), Term::var("Z")])
.then("Ancestor", vec![Term::var("X"), Term::var("Z")])
.build();
let result = chase(instance, &[rule1, rule2]);
assert!(result.terminated);
assert_eq!(result.instance.facts_for_predicate("Ancestor").len(), 3);
CLI
cargo run -- repl
cargo run -- gui
cargo run -- script examples/scripts/ancestor.chase
REPL language
fact Parent(alice, bob).
rule Parent(?X, ?Y) -> Ancestor(?X, ?Y).
schema Parent(parent, child).
sql SELECT * FROM Parent;
run.
query Ancestor(?X, ?Y)?
explain Ancestor(alice, carol)?
show facts
show rules
reset
help
Current SQL Slice
The repository now has a narrow SQL pipeline with:
- predicate-backed catalog inference
- relational schemas, rows, and values
- SQL parsing for a small subset
- logical planning
- execution for single-table queries and basic multi-table joins
Currently supported examples:
SELECT * FROM Parent
SELECT c0 FROM Parent
SELECT c0 FROM Parent WHERE c1 = 'bob'
SELECT c0 AS parent_name, 'seed' AS label FROM Parent
SELECT Parent.parent, Ancestor.child
FROM Parent, Ancestor
WHERE Parent.child = Ancestor.parent
In the REPL or script runner, use the sql command and end the statement with
;:
sql SELECT c0 FROM Parent WHERE c1 = 'bob';
You can also register stable column names for a predicate-backed table in the frontend before running SQL:
schema Parent(parent, child).
sql SELECT parent FROM Parent WHERE child = 'bob';
For multi-table queries, qualify column names with the table name:
schema Parent(parent, child).
schema Ancestor(parent, child).
sql SELECT Parent.parent, Ancestor.child FROM Parent, Ancestor WHERE Parent.child = Ancestor.parent;
Current limits:
- default column names are positional such as
c0,c1 - stable names require explicit catalog registration or
schema ...in the frontend - joins currently use comma-separated tables plus
WHEREfiltering - multi-table queries require qualified column names such as
Parent.child - no table aliases yet
- no aggregates
- projection aliases only via
AS
Development
For non-trivial changes, run:
cargo test
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --check
Notes
This repository is still centered on a rule-engine core. The new SQL-related modules are scaffolding for a broader query-engine direction, not a claim of feature-complete SQL support.
License
This project is licensed under BSD-3.