From c47d882fa544a6a370a3fe004fe38dd876ba70f5 Mon Sep 17 00:00:00 2001 From: zz_y Date: Sat, 18 Jul 2026 10:58:25 -0600 Subject: [PATCH] feat(control_plane): Phase 2 step 1 -- merge schema.rs onto asap-ir Column/ColumnId/DataType/Schema/CseError/cse_reuse_is_legal are now re-exported from asap_ir::intent_algebra::schema instead of defined locally. Unlike AggIntent's merge (Phase 1b), this needed no boundary- conversion layer: fresh diff showed asap_ir's version is a purely additive, backward-compatible superset of the pre-merge local type -- - Column gains `table: Option` (SQL join qualifier) + Column::new()/with_table() constructors. - Schema gains `closed: bool` (schema-on-read completeness flag) + column_id_qualified(). - DataType is byte-identical, no changes. Both new fields are #[serde(default)], confirmed backward-compatible by asap_ir's own tests. This is what made a full swap the right call here instead of Phase 1b's boundary-conversion approach for Column/DataType -- deleted agg_intent.rs's to_asap_column/from_asap_column/to_asap_dtype/ from_asap_dtype helpers, no longer needed once there's only one Column/DataType type. Blast radius was much smaller than the migration plan's stale ~38-site estimate: 15 real Column{}/Schema{} struct-literal construction sites across 9 files needed the new field added (table: None / closed: false) -- most of the original grep hits were field references or doc comments, not constructions. Verified: full workspace builds clean; control_plane's 820-test suite passes unchanged (same 1 pre-existing failure as #392, confirmed unrelated). Remaining Phase 2 work (docs/migration-plan-backend-plan.md): expr_ir.rs, query_expr.rs/relational.rs fresh diff, binder.rs/column_resolution.rs fresh diff, cse.rs move to L4, lower.rs, PromQL/SQL frontend retarget. --- .../src/intent_algebra/agg_intent.rs | 52 +-- control_plane/src/intent_algebra/binder.rs | 7 + .../src/intent_algebra/column_resolution.rs | 7 + control_plane/src/intent_algebra/cse.rs | 1 + .../src/intent_algebra/query_expr.rs | 3 + control_plane/src/intent_algebra/schema.rs | 364 ++---------------- control_plane/src/optimizer/cost/mod.rs | 1 + control_plane/src/optimizer/rules/mod.rs | 2 + .../src/physical/colored_dag/allocator.rs | 2 + .../src/physical/colored_dag/tests.rs | 3 + .../src/sketch_algebra/physical_expr.rs | 3 + .../sketch_algebra/rules/bind_archive_only.rs | 3 + control_plane/src/sketch_algebra/tests.rs | 1 + 13 files changed, 77 insertions(+), 372 deletions(-) diff --git a/control_plane/src/intent_algebra/agg_intent.rs b/control_plane/src/intent_algebra/agg_intent.rs index d84c145e..902c15f3 100644 --- a/control_plane/src/intent_algebra/agg_intent.rs +++ b/control_plane/src/intent_algebra/agg_intent.rs @@ -8,6 +8,12 @@ //! ASAPController has no tagged releases yet) and holds only what's //! genuinely control_plane-specific: //! +//! (Phase 2: `Column`/`DataType` are also re-exported from `asap_ir` now +//! — `schema.rs` got the full swap, not a boundary conversion, since +//! asap_ir's version turned out to be a purely additive, backward- +//! compatible superset. `output_column` below no longer needs a +//! conversion layer as a result.) +//! //! - **`frequency()` / `as_frequency()`** — control_plane's standalone //! point-frequency-via-CMS query (`count(*) WHERE key = k`), carried //! through the shared `AggIntent` as an `Extension` rather than a @@ -43,7 +49,6 @@ //! in scope, not off the intent. use asap_ir::intent_algebra::agg_accuracy as asap_agg_accuracy; -use asap_ir::intent_algebra::schema::{Column as AsapColumn, DataType as AsapDataType}; pub use asap_ir::intent_algebra::{ agg_is_exact, agg_is_mergeable, default_cardinality, default_quantile, is_frequency_heavy_hitter, ranking_measure, AggIntent, MathFunc, RankingMeasure, TimeFunc, @@ -54,43 +59,6 @@ use crate::types_v2::AccuracyTarget; const FREQUENCY_EXT_KIND: &str = "frequency"; -/// `control_plane::Column`/`DataType` and `asap_ir::Column`/`DataType` -/// are structurally identical but not the same type — merging `schema.rs` -/// itself is Phase 2 scope (it cascades into `Schema`/`QueryExpr`, used -/// pervasively; ~38 `Column{}` literals across this repo). Convert at -/// this boundary instead of widening this change. -fn to_asap_dtype(dt: &DataType) -> AsapDataType { - match dt { - DataType::Int64 => AsapDataType::Int64, - DataType::Float64 => AsapDataType::Float64, - DataType::Utf8 => AsapDataType::Utf8, - DataType::Bool => AsapDataType::Bool, - DataType::Timestamp => AsapDataType::Timestamp, - } -} - -fn from_asap_dtype(dt: &AsapDataType) -> DataType { - match dt { - AsapDataType::Int64 => DataType::Int64, - AsapDataType::Float64 => DataType::Float64, - AsapDataType::Utf8 => DataType::Utf8, - AsapDataType::Bool => DataType::Bool, - AsapDataType::Timestamp => DataType::Timestamp, - } -} - -fn to_asap_column(c: &Column) -> AsapColumn { - AsapColumn::new(c.name.clone(), to_asap_dtype(&c.dtype), c.nullable) -} - -fn from_asap_column(c: AsapColumn) -> Column { - Column { - name: c.name, - dtype: from_asap_dtype(&c.dtype), - nullable: c.nullable, - } -} - /// Construct control_plane's point-frequency-via-CMS intent. See module /// docs for why this is an `Extension`, not a shared first-class variant. pub fn frequency(accuracy: AccuracyTarget) -> AggIntent { @@ -190,11 +158,7 @@ pub fn archive_only(intent: &AggIntent) -> bool { /// `"frequency"` — that's control_plane-only knowledge). pub fn output_column(intent: &AggIntent, input: &Column) -> Column { if as_frequency(intent).is_some() { - return Column { - name: "frequency".into(), - dtype: DataType::Int64, - nullable: false, - }; + return Column::new("frequency", DataType::Int64, false); } - from_asap_column(intent.output_column(&to_asap_column(input))) + intent.output_column(input) } diff --git a/control_plane/src/intent_algebra/binder.rs b/control_plane/src/intent_algebra/binder.rs index 794e93e4..1f1f7495 100644 --- a/control_plane/src/intent_algebra/binder.rs +++ b/control_plane/src/intent_algebra/binder.rs @@ -137,6 +137,7 @@ impl Binder { name, dtype: DataType::Utf8, // labels / group keys are strings nullable: true, + table: None, }); } } @@ -146,6 +147,7 @@ impl Binder { columns, time_index, unique_keys: Vec::new(), + closed: false, } } } @@ -157,11 +159,13 @@ fn default_leaf_columns() -> Vec { name: "ts".into(), dtype: DataType::Timestamp, nullable: false, + table: None, }, Column { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }, ] } @@ -269,16 +273,19 @@ mod tests { name: "ts".into(), dtype: DataType::Timestamp, nullable: false, + table: None, }, Column { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }, Column { name: "datacenter".into(), dtype: DataType::Utf8, nullable: false, + table: None, }, ]) } else { diff --git a/control_plane/src/intent_algebra/column_resolution.rs b/control_plane/src/intent_algebra/column_resolution.rs index 89357c7c..744abc47 100644 --- a/control_plane/src/intent_algebra/column_resolution.rs +++ b/control_plane/src/intent_algebra/column_resolution.rs @@ -91,11 +91,13 @@ pub fn infer_source_schema(_metric_or_table_name: &str) -> Schema { name: "ts".into(), dtype: DataType::Timestamp, nullable: false, + table: None, }, Column { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }, ], 0, @@ -265,6 +267,7 @@ pub fn output_schema_for_aggregate(input: &Schema, by: &[ColumnId], aggs: &[AggI name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }); for intent in aggs { out_cols.push(crate::intent_algebra::output_column(intent, &probe)); @@ -280,6 +283,7 @@ pub fn output_schema_for_aggregate(input: &Schema, by: &[ColumnId], aggs: &[AggI columns: out_cols, time_index: None, unique_keys, + closed: false, } } @@ -402,11 +406,13 @@ mod tests { name: "host".into(), dtype: DataType::Utf8, nullable: false, + table: None, }); input.columns.push(Column { name: "region".into(), dtype: DataType::Utf8, nullable: false, + table: None, }); // Group by host, region (positions 2 and 3). let by = vec![2usize, 3usize]; @@ -439,6 +445,7 @@ mod tests { name: "host".into(), dtype: DataType::Utf8, nullable: false, + table: None, }); let ids = resolve_named_keys(&["host".to_string()], &s).unwrap(); assert_eq!(ids, vec![2usize]); diff --git a/control_plane/src/intent_algebra/cse.rs b/control_plane/src/intent_algebra/cse.rs index 0863d6ac..62c91465 100644 --- a/control_plane/src/intent_algebra/cse.rs +++ b/control_plane/src/intent_algebra/cse.rs @@ -170,6 +170,7 @@ mod tests { name: name.into(), dtype, nullable: false, + table: None, } } diff --git a/control_plane/src/intent_algebra/query_expr.rs b/control_plane/src/intent_algebra/query_expr.rs index cb3aed3d..c54ccacb 100644 --- a/control_plane/src/intent_algebra/query_expr.rs +++ b/control_plane/src/intent_algebra/query_expr.rs @@ -560,6 +560,7 @@ impl QueryExpr { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }); for intent in aggs { out_cols.push(crate::intent_algebra::output_column(intent, &probe)); @@ -578,6 +579,7 @@ impl QueryExpr { columns: out_cols, time_index: None, unique_keys, + closed: false, }) } QueryExpr::LetBinding { name, expr, child } => { @@ -794,6 +796,7 @@ mod tests { name: name.into(), dtype, nullable: false, + table: None, } } diff --git a/control_plane/src/intent_algebra/schema.rs b/control_plane/src/intent_algebra/schema.rs index 0a895cce..75d2d989 100644 --- a/control_plane/src/intent_algebra/schema.rs +++ b/control_plane/src/intent_algebra/schema.rs @@ -1,332 +1,40 @@ //! Layer 3 schema flow — every L3 edge carries a typed `Schema`. //! -//! Per `control_plane/docs/design.md` §6 "Schema flow — every L3 edge carries -//! a typed schema". The DAG is type-checked: a node's output schema is a -//! function of its inputs and parameters and is verifiable independently -//! of the surrounding context. +//! ## Phase 2 (docs/migration-plan-backend-plan.md) //! -//! `Schema::unique_keys` is the load-bearing field for the workload-level -//! CSE pass (`design.md` §6 "DAG, not tree" + the batched-queries example -//! around line ~1284). Two `QueryExpr::Ref` consumers can share a producer -//! only when its output schema is provably stable across reads — the -//! unique-key metadata is what lets the deduper assert that. +//! `Column` / `ColumnId` / `DataType` / `Schema` / `CseError` / +//! `cse_reuse_is_legal` are no longer defined in this repo — re-exported +//! from `asap_ir::intent_algebra::schema`. Unlike `AggIntent`'s merge +//! (Phase 1b), this one needed no boundary-conversion layer: asap_ir's +//! version is a pure additive superset of control_plane's pre-merge +//! version — //! -//! Single-query plans don't read this field; it lives here so the metadata -//! is available the moment workload-aware planning lands without requiring -//! an L3-wide schema change. - -#![allow(dead_code)] - -use serde::{Deserialize, Serialize}; - -/// Index into [`Schema::columns`] used everywhere a column position is -/// referenced (group-by keys, unique-key sets, the time axis index). -/// -/// Aliased to `usize` to match `design.md`'s `Vec>` for -/// `unique_keys`. Kept as a named type so downstream code can pattern on -/// the intent ("this is a column position, not just any number"). -pub type ColumnId = usize; - -/// One column in a [`Schema`]. Mirrors `design.md` §6 `Field` — -/// `name + dtype + nullable`. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Column { - /// Column name as it appears in the producer's output. PromQL leaves - /// produce label-name + the synthetic `value` / `timestamp` columns; - /// SQL leaves carry their `information_schema` names. - pub name: String, - /// Column data type. Kept narrow at L3 (`Int64` / `Float64` / `Utf8` - /// / `Bool` / `Timestamp`); `Sketch(...)` is an L4-only addition per - /// design.md §6.4 and is intentionally absent here. - pub dtype: DataType, - /// Whether NULL values are allowed in this column. PromQL value - /// columns are non-nullable; SQL columns inherit their DDL nullability. - pub nullable: bool, -} - -/// L3 column data types. Deliberately narrow: no sketch state at this -/// layer (see `design.md` §6.4 for the L4 `DataType::Sketch(...)` -/// extension). -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum DataType { - /// 64-bit signed integer. Counter columns, group-cardinality outputs. - Int64, - /// 64-bit IEEE-754 float. Quantile / Avg / Sum-over-floats output. - Float64, - /// UTF-8 string. PromQL label values, SQL `VARCHAR` / `TEXT`. - Utf8, - /// Boolean — predicate output, `unless` / `and` / `or` PromQL ops. - Bool, - /// Wall-clock timestamp. PromQL leaves carry exactly one of these - /// (the `time_index` column); SQL leaves may or may not. - Timestamp, -} - -/// Per-edge L3 schema. Flowing between any two L3 operators, on every -/// node's input and output. -/// -/// `unique_keys` is metadata for reuse-aware planning: each inner `Vec` -/// is a set of column indices that together uniquely identify rows. The -/// outer `Vec` allows multiple unique-key sets (primary key + another -/// unique constraint). Populated by per-node input/output spec — -/// `Aggregate { by, .. }` emits `unique_keys = [by]`; `Distinct { cols }` -/// adds `cols`; most other nodes pass through. -/// -/// **Consumed by**: workload-level CSE (`CostModel::workload_cost` in the -/// design, not yet shipped). The single-query path, the `Bind*` rules, -/// push-down, and L5 emitters do not read this field. -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct Schema { - /// Columns flowing on this edge, in positional order. - pub columns: Vec, - /// Index into `columns` for the time axis, if any. PromQL leaves - /// always carry one; SQL leaves may or may not. - #[serde(default)] - pub time_index: Option, - /// Unique-key sets — each inner vec is a tuple of column indices - /// that together uniquely identifies a row. Empty `Vec` means - /// "no provable unique constraint" (the conservative default). - #[serde(default)] - pub unique_keys: Vec>, -} - -impl Schema { - /// Construct a `Schema` from columns alone — no time index, no - /// unique-key constraint. Used by `Scan` over a tabular source - /// when the catalog supplies no primary-key metadata. - pub fn new(columns: Vec) -> Self { - Self { - columns, - time_index: None, - unique_keys: Vec::new(), - } - } - - /// Construct a `Scan`-style schema with explicit `time_index` + - /// inferred unique keys (e.g. PromQL leaves: `[time_index, label_set]`). - pub fn with_time_index( - columns: Vec, - time_index: ColumnId, - unique_keys: Vec>, - ) -> Self { - Self { - columns, - time_index: Some(time_index), - unique_keys, - } - } - - /// Look up a column by name. `None` if not present. - pub fn column_id(&self, name: &str) -> Option { - self.columns.iter().position(|c| c.name == name) - } - - /// Whether this schema has *any* provable unique key. The CSE pass - /// reads this to decide whether two `Ref` consumers can safely share - /// a producer (see `design.md` §6 line ~1284 + the unit test in - /// `tests::cse_substitution_legal_only_with_unique_keys`). - pub fn has_unique_key(&self) -> bool { - !self.unique_keys.is_empty() - } - - /// Append `cols` as an additional unique-key set if not already present. - /// Used by `Distinct { cols }` per design.md §6 schema-flow table: - /// "the input schema with `unique_keys` tightened to include `cols`". - pub fn add_unique_key(&mut self, cols: Vec) { - if !self.unique_keys.contains(&cols) { - self.unique_keys.push(cols); - } - } -} - -// ── CSE legality (the load-bearing consumer of `unique_keys`) ──────────────── -// -// Phase F per `control_plane/docs/design.md` §6 Schema flow + the batched- -// queries example (§6 line ~1320): -// -// "CSE legality leans on `Schema::unique_keys` (§6 Schema flow): two -// `QueryExpr::Ref` consumers can share a producer only when its -// output schema is provably stable across reads — the unique-key -// metadata is what lets the deduper assert that without re-running -// 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. - -use thiserror::Error; - -/// Errors returned by [`cse_reuse_is_legal`] when shared-producer reuse -/// would violate the design's stability invariant. -#[derive(Debug, Error, PartialEq, Eq)] -pub enum CseError { - /// Producer schema lacks any `unique_keys` set — row identity is - /// not provably stable across reads, so two `Ref` consumers cannot - /// safely share it. The deduper falls back to per-consumer - /// recomputation. Per design.md §6 line ~1356. - #[error( - "shared-producer CSE refused: producer schema has no unique_keys \ - (design.md §6 schema-flow — without a provable unique key the \ - deduper cannot assert row identity across reads)" - )] - NoUniqueKeys, - /// Trivially-callable case: only one consumer means no reuse to - /// gate. Returned so the caller can short-circuit instead of - /// emitting a degenerate `LetBinding`. - #[error("CSE not applicable: {0} consumer(s) — need ≥ 2 for shared-producer reuse")] - InsufficientConsumers(usize), -} - -/// Two `QueryExpr::Ref` nodes can share a producer (same `LetBinding`) -/// only when the producer's output schema has stable per-row identity — -/// i.e. `Schema::unique_keys` is non-empty. This is the gatekeeper: -/// returns `Ok(())` if shared-producer reuse is legal, otherwise `Err`. -/// -/// Per design.md §6 line ~1356 — `unique_keys` is what makes CSE -/// provably correct. The deduper consults this before emitting a -/// `LetBinding`, and `CostModel::workload_cost` only credits a shared -/// binding when this gate has fired green. -/// -/// `consumer_count` is the number of `QueryExpr::Ref { name }` sites the -/// deduper has identified for the candidate binding. Single-consumer -/// cases short-circuit with `InsufficientConsumers` — a `LetBinding` -/// with one `Ref` is just a no-op alias and shouldn't be hoisted. -pub fn cse_reuse_is_legal(producer_schema: &Schema, consumer_count: usize) -> Result<(), CseError> { - if consumer_count < 2 { - return Err(CseError::InsufficientConsumers(consumer_count)); - } - if !producer_schema.has_unique_key() { - return Err(CseError::NoUniqueKeys); - } - Ok(()) -} - -// ── Tests ───────────────────────────────────────────────────────────────────── - -#[cfg(test)] -mod tests { - use super::*; - - fn col(name: &str, dtype: DataType) -> Column { - Column { - name: name.into(), - dtype, - nullable: false, - } - } - - /// `cse_reuse_is_legal` accepts a producer schema with at least one - /// `unique_keys` set + ≥2 consumers. This is the design.md §6 - /// "load-bearing" green path. - #[test] - fn cse_reuse_legal_when_unique_keys_set() { - let producer = Schema::with_time_index( - vec![ - col("ts", DataType::Timestamp), - col("service", DataType::Utf8), - col("value", DataType::Float64), - ], - 0, - vec![vec![0, 1]], - ); - assert_eq!(cse_reuse_is_legal(&producer, 2), Ok(())); - assert_eq!(cse_reuse_is_legal(&producer, 5), Ok(())); - } - - /// Schema without `unique_keys` is the conservative-default case — - /// the deduper must refuse to share it. Pins design.md §6 line - /// ~1356 ("Without it, the deduper has to be conservative and reuse - /// drops on the floor"). - #[test] - fn cse_reuse_illegal_when_unique_keys_empty() { - let producer = Schema::new(vec![col("a", DataType::Int64), col("b", DataType::Float64)]); - assert_eq!( - cse_reuse_is_legal(&producer, 2), - Err(CseError::NoUniqueKeys) - ); - } - - /// Single-consumer case is short-circuited — no `LetBinding` should - /// be emitted for one `Ref` because there's no reuse to credit. - #[test] - fn cse_reuse_rejects_single_consumer() { - let producer = Schema::with_time_index( - vec![col("ts", DataType::Timestamp), col("v", DataType::Float64)], - 0, - vec![vec![0]], - ); - assert_eq!( - cse_reuse_is_legal(&producer, 1), - Err(CseError::InsufficientConsumers(1)) - ); - assert_eq!( - cse_reuse_is_legal(&producer, 0), - Err(CseError::InsufficientConsumers(0)) - ); - } - - /// Empty `unique_keys` rejection takes precedence over the consumer - /// count check only when both pass — but here we verify the - /// insufficient-consumers branch fires first (a defensive ordering - /// so callers see the clearer error when they get the call wrong). - #[test] - fn cse_reuse_consumer_check_precedes_unique_key_check() { - let producer = Schema::new(vec![col("a", DataType::Int64)]); - // Both conditions fail; consumer check is reported. - assert_eq!( - cse_reuse_is_legal(&producer, 1), - Err(CseError::InsufficientConsumers(1)) - ); - } - - #[test] - fn schema_new_has_no_time_or_unique_key() { - let s = Schema::new(vec![col("k", DataType::Utf8), col("v", DataType::Float64)]); - assert!(s.time_index.is_none()); - assert!(!s.has_unique_key()); - assert_eq!(s.column_id("k"), Some(0)); - assert_eq!(s.column_id("v"), Some(1)); - assert_eq!(s.column_id("missing"), None); - } - - #[test] - fn schema_with_time_index_populates_metadata() { - let s = Schema::with_time_index( - vec![ - col("ts", DataType::Timestamp), - col("service", DataType::Utf8), - col("value", DataType::Float64), - ], - 0, - vec![vec![0, 1]], - ); - assert_eq!(s.time_index, Some(0)); - assert!(s.has_unique_key()); - assert_eq!(s.unique_keys, vec![vec![0, 1]]); - } - - #[test] - fn add_unique_key_dedupes() { - let mut s = Schema::new(vec![col("a", DataType::Utf8), col("b", DataType::Utf8)]); - s.add_unique_key(vec![0]); - s.add_unique_key(vec![0]); - s.add_unique_key(vec![0, 1]); - assert_eq!(s.unique_keys, vec![vec![0], vec![0, 1]]); - } - - #[test] - fn schema_serde_roundtrip() { - let s = Schema::with_time_index( - vec![ - col("ts", DataType::Timestamp), - col("value", DataType::Float64), - ], - 0, - vec![vec![0]], - ); - let json = serde_json::to_string(&s).unwrap(); - let back: Schema = serde_json::from_str(&json).unwrap(); - assert_eq!(s, back); - } -} +//! - `Column` gains `table: Option` (table/alias qualifier for +//! SQL `t.col` disambiguation across joins) plus `Column::new()` / +//! `Column::with_table()` constructors. +//! - `Schema` gains `closed: bool` (schema-on-read completeness flag, +//! Apache Calcite `DynamicRecordType`-style) plus +//! `column_id_qualified()`. +//! +//! Both new fields are `#[serde(default)]`, confirmed backward-compatible +//! by asap_ir's own tests (`schema_closed_defaults_to_open_when_absent`, +//! `column_table_defaults_to_none_when_absent`) — no wire-format break, +//! unlike `AccuracyTarget`'s tag-shape change in Phase 1b. This is what +//! made a full swap the obvious call here instead of converting at a +//! boundary: `agg_intent.rs`'s `to_asap_column`/`from_asap_column`/ +//! `to_asap_dtype`/`from_asap_dtype` helpers from Phase 1b are deleted — +//! no longer needed once there's only one `Column`/`DataType` type. +//! +//! `DataType` itself was already byte-identical between the two repos; +//! no changes there at all. +//! +//! Blast radius from the swap: every `Column { .. }` / `Schema { .. }` +//! struct literal across this repo (~38 `Column{}` sites) needs the new +//! field addressed — either via the `table`/`closed` field explicitly, +//! or (preferred where the call site doesn't care) `Column::new(..)` / +//! `Schema::new(..)` / `Schema::with_time_index(..)`, which default the +//! new fields the same way the pre-merge constructors did. + +pub use asap_ir::intent_algebra::schema::{ + cse_reuse_is_legal, Column, ColumnId, CseError, DataType, Schema, +}; diff --git a/control_plane/src/optimizer/cost/mod.rs b/control_plane/src/optimizer/cost/mod.rs index c60a5be1..983f7438 100644 --- a/control_plane/src/optimizer/cost/mod.rs +++ b/control_plane/src/optimizer/cost/mod.rs @@ -744,6 +744,7 @@ mod workload_cost_tests { name: name.into(), dtype, nullable: false, + table: None, } } diff --git a/control_plane/src/optimizer/rules/mod.rs b/control_plane/src/optimizer/rules/mod.rs index 08fe6dba..deb2aa48 100644 --- a/control_plane/src/optimizer/rules/mod.rs +++ b/control_plane/src/optimizer/rules/mod.rs @@ -212,11 +212,13 @@ pub fn bind_workload_typed(w: &QueryWorkload) -> Option) -> QueryExpr { name: "ts".into(), dtype: DataType::Timestamp, nullable: false, + table: None, }, Column { name: "service".into(), dtype: DataType::Utf8, nullable: false, + table: None, }, Column { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }, ], 0, diff --git a/control_plane/src/sketch_algebra/physical_expr.rs b/control_plane/src/sketch_algebra/physical_expr.rs index 90a24b03..143b9ceb 100644 --- a/control_plane/src/sketch_algebra/physical_expr.rs +++ b/control_plane/src/sketch_algebra/physical_expr.rs @@ -266,16 +266,19 @@ mod tests { name: "ts".into(), dtype: DataType::Timestamp, nullable: false, + table: None, }, Column { name: "service".into(), dtype: DataType::Utf8, nullable: false, + table: None, }, Column { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }, ], 0, diff --git a/control_plane/src/sketch_algebra/rules/bind_archive_only.rs b/control_plane/src/sketch_algebra/rules/bind_archive_only.rs index 1747d7dc..ed9776e4 100644 --- a/control_plane/src/sketch_algebra/rules/bind_archive_only.rs +++ b/control_plane/src/sketch_algebra/rules/bind_archive_only.rs @@ -86,16 +86,19 @@ mod tests { name: "ts".into(), dtype: DataType::Timestamp, nullable: false, + table: None, }, Column { name: "service".into(), dtype: DataType::Utf8, nullable: false, + table: None, }, Column { name: "value".into(), dtype: DataType::Float64, nullable: false, + table: None, }, ], 0, diff --git a/control_plane/src/sketch_algebra/tests.rs b/control_plane/src/sketch_algebra/tests.rs index 8a50a65f..db661b2b 100644 --- a/control_plane/src/sketch_algebra/tests.rs +++ b/control_plane/src/sketch_algebra/tests.rs @@ -21,6 +21,7 @@ fn col(name: &str, dtype: DataType) -> Column { name: name.into(), dtype, nullable: false, + table: None, } }