Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"crates/core",
"crates/plan",
"crates/lower",
"crates/e2e",
]
Expand Down
7 changes: 4 additions & 3 deletions crates/core/src/intent_algebra/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
//! - [`query_expr`] — the canonical, language- and deployment-independent L3
//! intent algebra ([`QueryExpr`] + [`AggIntent`]), with positional
//! [`ColumnId`] schema flow.
//! - [`cse`] — workload-level common-sub-expression elimination over L3.
//!
//! Workload-level common-sub-expression elimination over L3 lives in the
//! optimizer layer (`asap-plan`), not here — the IR crate stays free of
//! cost-aware passes.

pub mod agg_intent;
pub mod binder;
pub mod column_resolution;
pub mod cse;
pub mod expr_ir;
pub mod lower;
pub mod names;
Expand All @@ -29,7 +31,6 @@ pub use column_resolution::{
infer_schema_for_root, infer_source_schema, output_schema_for_aggregate, resolve_column_ref,
resolve_column_refs, resolve_expr, ResolveError,
};
pub use cse::{dedupe_subtrees, CseWorkloadPlan};
pub use expr_ir::{ArithOp, ColumnRef, CompareOp, Expr, L2Expr, L3Expr, L3Scalar};
pub use lower::{convert, convert_root, ConvertError};
pub use names::{BindingName, QueryId};
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/intent_algebra/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ impl Schema {
// the producer's logic."
//
// `cse_reuse_is_legal` is the gatekeeper. The workload-level CSE pass
// (`intent_algebra::cse::dedupe_subtrees`) consults it before emitting
// a `LetBinding` to share a producer between ≥2 `Ref` consumers.
// (`asap_plan::cse::dedupe_subtrees`) consults it before emitting a
// `LetBinding` to share a producer between ≥2 `Ref` consumers.

use thiserror::Error;

Expand Down
9 changes: 9 additions & 0 deletions crates/plan/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "asap-plan"
version = "0.1.0"
edition = "2021"

# The cost-aware optimizer layer (L4 decisions) over the L3 intent algebra.
# Depends only on the IR crate — never on a front end.
[dependencies]
asap-control-core = { path = "../core" }
9 changes: 9 additions & 0 deletions crates/plan/src/boundary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Sketch-vs-exact boundary (stub).
//!
//! The per-node accuracy decision — whether an approximate intent
//! (`Quantile`/`Cardinality`/`Count`/`TopK`) is realised by an exact operator
//! or a sketch, and with which parameters. This is an L4 concern: L3 carries
//! only the intent + accuracy target, never the realization.
//!
//! TODO(#34, cross-cutting): confirm the boundary decision fires per node over
//! nested trees once this layer is fleshed out.
11 changes: 11 additions & 0 deletions crates/plan/src/canonicalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Post-lowering canonicalization (stub).
//!
//! A single normalization pass both front ends run their L3 output through, so
//! semantically-equivalent SQL and PromQL produce **identical** L3 (e.g. the
//! heavy-hitter TopK recognition that is currently duplicated across the two
//! lowerers). Owning it here — above both front ends, over the shared IR — is
//! what lets a future language path inherit the normalization for free.
//!
//! TODO(#34): implement `canonicalize(expr: QueryExpr) -> QueryExpr` and route
//! both `lower_promql` / `lower_sql` outputs through it; add the cross-language
//! equivalence tests that pin the canonical form.
7 changes: 7 additions & 0 deletions crates/plan/src/cost_model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Cost model (stub).
//!
//! The cost traits the optimizer consults — and, in particular, the model that
//! credits a hoisted [`cse`](crate::cse) producer once instead of per consumer.
//!
//! TODO(#6): wire workload-level CSE into a cost model.
//! TODO(#33): detect which optimizations are applicable to a query workload.
16 changes: 8 additions & 8 deletions crates/core/src/intent_algebra/cse.rs → crates/plan/src/cse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
//!
//! Multi-root planning hoists shared sub-DAGs into `LetBinding`s so the cost
//! model can credit the producer once. Legality is gated by
//! [`cse_reuse_is_legal`](super::schema::cse_reuse_is_legal): a candidate
//! [`cse_reuse_is_legal`](asap_control_core::intent_algebra::schema::cse_reuse_is_legal): a candidate
//! sub-DAG only becomes a binding when its output schema has at least one
//! `unique_keys` set — the load-bearing field for this pass.
//!
//! Scope: the basic "≥2 roots with identical `Aggregate`-child sub-trees"
//! case. The fully-general algorithm (alpha-equivalence, schema-merge,
//! nested CSE) is a downstream optimisation, not part of the IR contract.

use crate::intent_algebra::names::{BindingName, QueryId};
use crate::intent_algebra::query_expr::QueryExpr;
use crate::intent_algebra::schema::cse_reuse_is_legal;
use asap_control_core::intent_algebra::names::{BindingName, QueryId};
use asap_control_core::intent_algebra::query_expr::QueryExpr;
use asap_control_core::intent_algebra::schema::cse_reuse_is_legal;

/// Multi-root container produced by the CSE pass.
#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -115,10 +115,10 @@ pub fn dedupe_subtrees(roots: Vec<(QueryId, QueryExpr)>) -> CseWorkloadPlan {
#[cfg(test)]
mod tests {
use super::*;
use crate::intent_algebra::agg_intent::AggIntent;
use crate::intent_algebra::query_expr::{Source, WindowKind};
use crate::intent_algebra::schema::{Column, DataType, Schema};
use crate::types::AccuracyTarget;
use asap_control_core::intent_algebra::agg_intent::AggIntent;
use asap_control_core::intent_algebra::query_expr::{Source, WindowKind};
use asap_control_core::intent_algebra::schema::{Column, DataType, Schema};
use asap_control_core::types::AccuracyTarget;
use std::time::Duration;

fn col(name: &str, dtype: DataType) -> Column {
Expand Down
30 changes: 30 additions & 0 deletions crates/plan/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! `asap-plan` — the cost-aware optimizer layer over the L3 intent algebra.
//!
//! This crate sits between the language-agnostic IR ([`asap_control_core`]) and
//! any runtime: it consumes L3 [`QueryExpr`](asap_control_core::intent_algebra::QueryExpr)
//! trees and makes the cost-aware decisions that L3 deliberately leaves open —
//! which shared sub-expressions to hoist, which sketch (if any) realises each
//! approximate intent, and the canonical form both front ends should agree on.
//!
//! It depends only on the IR crate, never on a front end — the layering
//! invariant (arrows point up) holds here too.
//!
//! ## Status
//!
//! Landed today as a **placeholder** with one real occupant, [`cse`]
//! (workload-level common-sub-expression elimination). The remaining modules
//! are intentional stubs marking where the optimizer work lands:
//!
//! - [`cost_model`] — cost traits + the model CSE credits a shared producer
//! against (issue #6).
//! - [`boundary`] — the per-node sketch-vs-exact (accuracy) decision (issue #34
//! cross-cutting item; an L4 concern, not carried in L3).
//! - [`canonicalize`] — a single post-lowering normalization pass both front
//! ends run through, so semantically-equal SQL and PromQL produce identical
//! L3 (issue #34).

pub mod cse;

pub mod boundary;
pub mod cost_model;
pub mod canonicalize;
Loading