diff --git a/control_plane/src/physical/compiler.rs b/control_plane/src/physical/compiler.rs index 52a80a55..72ef55df 100644 --- a/control_plane/src/physical/compiler.rs +++ b/control_plane/src/physical/compiler.rs @@ -3576,7 +3576,7 @@ pub(crate) fn aggregation_config_for_materialization( asap_types::PrecomputeMaterialization::from_yaml_data( &yaml, None, - asap_types::QueryLanguage::promql, + asap_types::QueryLanguage::PromQl, ) .context("build materialization from physical aggregation") } diff --git a/control_plane/src/pipeline.rs b/control_plane/src/pipeline.rs index aa93fcbe..dbbf65ad 100644 --- a/control_plane/src/pipeline.rs +++ b/control_plane/src/pipeline.rs @@ -764,7 +764,7 @@ mod tests { }"#; let spec: QuerySpec = serde_json::from_str(json).unwrap(); assert_eq!(spec.id.as_ref().unwrap().as_str(), "q-001"); - assert_eq!(spec.language, Some(QueryLanguage::PromQL)); + assert_eq!(spec.language, Some(QueryLanguage::PromQl)); assert_eq!(spec.accuracy, Some(AccuracyTarget::Epsilon(0.02))); assert_eq!(spec.dollars, Some(0.001)); assert_eq!(spec.deployment_model.as_deref(), Some("asaplifecycle")); diff --git a/control_plane/src/query_plan.rs b/control_plane/src/query_plan.rs index 5a9d129b..ac546bd3 100644 --- a/control_plane/src/query_plan.rs +++ b/control_plane/src/query_plan.rs @@ -15,6 +15,7 @@ use planner_types::pre_asap::Reduction; use serde::{Deserialize, Serialize}; use thiserror::Error; +pub use asap_types::QueryLanguage; use asap_types::{sds::SummaryDefinitionId, PolicyFingerprint}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -204,15 +205,6 @@ impl QueryPlan { } } -#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum QueryLanguage { - #[default] - PromQl, - MetricsQl, - ClickHouseSql, -} - /// Stable identity inside one query entry. Edges are IDs so common /// subexpressions remain shared after serialization. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/control_plane/src/types_v2.rs b/control_plane/src/types_v2.rs index 4ee501e9..6e38595d 100644 --- a/control_plane/src/types_v2.rs +++ b/control_plane/src/types_v2.rs @@ -29,19 +29,7 @@ use std::time::Duration; use serde::{Deserialize, Serialize}; -// ── QueryLanguage ───────────────────────────────────────────────────────────── - -/// Source language the raw query string is written in. Drives which L1 -/// parser the control plane dispatches to. -/// -/// The control plane consumes `PromQL` only (see `query_parser/promql.rs`). -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] -pub enum QueryLanguage { - /// Prometheus query language. Parsed via `promql-parser`. - #[serde(rename = "prom_ql", alias = "prom_q_l")] - PromQL, -} +pub use asap_types::QueryLanguage; // ── AccuracyTarget ──────────────────────────────────────────────────────────── @@ -207,7 +195,7 @@ mod tests { #[test] fn query_language_serde_roundtrip() { - let variant = QueryLanguage::PromQL; + let variant = QueryLanguage::PromQl; let json = serde_json::to_string(&variant).unwrap(); let back: QueryLanguage = serde_json::from_str(&json).unwrap(); assert_eq!(variant, back, "round-trip failed for {variant:?}"); diff --git a/crates/asap_types/src/aggregation_config.rs b/crates/asap_types/src/aggregation_config.rs index dba4da16..ce66d9b9 100644 --- a/crates/asap_types/src/aggregation_config.rs +++ b/crates/asap_types/src/aggregation_config.rs @@ -335,15 +335,27 @@ impl PrecomputeMaterialization { .unwrap_or("") .to_string(); - // Only PromQL is supported after the dead-code cleanup. let (metric, table_name, value_column) = match query_language { - QueryLanguage::promql => { + QueryLanguage::PromQl | QueryLanguage::MetricsQl => { let metric = aggregation_data["metric"] .as_str() - .ok_or_else(|| anyhow::anyhow!("Missing metric for PromQL query language"))? + .ok_or_else(|| { + anyhow::anyhow!("Missing metric for time-series query language") + })? .to_string(); (metric, None, None) } + QueryLanguage::ClickHouseSql => { + let table = aggregation_data["tableName"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Missing tableName for ClickHouse SQL"))? + .to_string(); + let column = aggregation_data["valueColumn"] + .as_str() + .ok_or_else(|| anyhow::anyhow!("Missing valueColumn for ClickHouse SQL"))? + .to_string(); + (format!("{table}.{column}"), Some(table), Some(column)) + } }; let mut config = Self::new( @@ -427,10 +439,10 @@ mod tests { #[test] fn explicit_aggregation_id_in_yaml_is_ignored() { let with = - AggregationConfig::from_yaml_data(&sample_yaml(true), None, QueryLanguage::promql) + AggregationConfig::from_yaml_data(&sample_yaml(true), None, QueryLanguage::PromQl) .expect("parse ok"); let without = - AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::promql) + AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::PromQl) .expect("parse ok"); assert_eq!( with.policy_fingerprint(), @@ -442,9 +454,9 @@ mod tests { /// Round-tripping the same content yields the same fingerprint. #[test] fn fingerprint_is_deterministic_per_content() { - let a = AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::promql) + let a = AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::PromQl) .expect("parse a"); - let b = AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::promql) + let b = AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::PromQl) .expect("parse b"); assert_eq!(a.policy_fingerprint(), b.policy_fingerprint()); assert_ne!( @@ -457,7 +469,7 @@ mod tests { #[test] fn pane_origin_round_trips_and_changes_definition_identity() { let mut epoch = - AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::promql) + AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::PromQl) .expect("parse"); let unknown = epoch.policy_fingerprint(); epoch.pane_origin_ms = Some(7_000); @@ -493,7 +505,7 @@ mod tests { #[test] fn policy_fp_u64_accessor_equals_fingerprint_u64() { let cfg = - AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::promql) + AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::PromQl) .expect("parse"); assert_eq!(cfg.policy_fp_u64(), cfg.policy_fingerprint().as_u64()); } @@ -502,7 +514,7 @@ mod tests { #[test] fn serialize_to_json_omits_aggregation_id() { let cfg = - AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::promql) + AggregationConfig::from_yaml_data(&sample_yaml(false), None, QueryLanguage::PromQl) .expect("parse"); let json = cfg.serialize_to_json(); assert!( diff --git a/crates/asap_types/src/enums.rs b/crates/asap_types/src/enums.rs index e72d4476..05bf9171 100644 --- a/crates/asap_types/src/enums.rs +++ b/crates/asap_types/src/enums.rs @@ -77,11 +77,25 @@ impl FromStr for Statistic { } } -#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq)] -#[allow(non_camel_case_types)] +#[derive( + clap::ValueEnum, + Clone, + Copy, + Debug, + Default, + PartialEq, + Eq, + Hash, + serde::Serialize, + serde::Deserialize, +)] +#[serde(rename_all = "snake_case")] pub enum QueryLanguage { - #[value(alias = "PROMQL")] - promql, + #[default] + #[value(alias = "PROMQL", alias = "promql")] + PromQl, + MetricsQl, + ClickHouseSql, } /// Policy for cleaning up old aggregates from the store. diff --git a/crates/asap_types/src/sds.rs b/crates/asap_types/src/sds.rs index 3c058e59..dc08cee6 100644 --- a/crates/asap_types/src/sds.rs +++ b/crates/asap_types/src/sds.rs @@ -986,7 +986,7 @@ mod tests { fn configured_identity_preserves_heap_hydra_and_subtype_and_excludes_population() { let yaml:serde_yaml::Value=serde_yaml::from_str("aggregationType: DDSketch\naggregationSubType: ''\nmetric: m\nlabels:\n grouping: []\n rollup: []\n aggregated: []\nparameters:\n relative_accuracy: 0.01\nwindowSize: 30\nwindowType: tumbling\nspatialFilter: ''\n").unwrap(); let mut config = - PrecomputeMaterialization::from_yaml_data(&yaml, None, crate::QueryLanguage::promql) + PrecomputeMaterialization::from_yaml_data(&yaml, None, crate::QueryLanguage::PromQl) .unwrap(); assert!(matches!( SummaryDescriptor::from_config(&config).unwrap().fidelity, @@ -1025,7 +1025,7 @@ mod tests { ) .unwrap(); let config = - PrecomputeMaterialization::from_yaml_data(&yaml, None, crate::QueryLanguage::promql) + PrecomputeMaterialization::from_yaml_data(&yaml, None, crate::QueryLanguage::PromQl) .unwrap(); assert!(matches!( SummaryDescriptor::from_config(&config).unwrap().fidelity, diff --git a/data_plane/src/drivers/query/adapters/config.rs b/data_plane/src/drivers/query/adapters/config.rs index 465b249a..4ee712b9 100644 --- a/data_plane/src/drivers/query/adapters/config.rs +++ b/data_plane/src/drivers/query/adapters/config.rs @@ -55,7 +55,7 @@ impl AdapterConfig { Self::new( QueryProtocol::PrometheusHttp, - QueryLanguage::promql, + QueryLanguage::PromQl, fallback, ) } @@ -67,7 +67,7 @@ impl AdapterConfig { use crate::drivers::query::fallback::VictoriaMetricsHttpFallback; Self::new( QueryProtocol::PrometheusHttp, - QueryLanguage::promql, + QueryLanguage::PromQl, Some(Arc::new(VictoriaMetricsHttpFallback::new(fallback_url))), ) } diff --git a/data_plane/src/drivers/query/adapters/prometheus_http.rs b/data_plane/src/drivers/query/adapters/prometheus_http.rs index 6fcc4f03..77310f08 100644 --- a/data_plane/src/drivers/query/adapters/prometheus_http.rs +++ b/data_plane/src/drivers/query/adapters/prometheus_http.rs @@ -417,7 +417,7 @@ mod tests { use crate::storage_engines::types::enums::{QueryLanguage, QueryProtocol}; fn create_test_adapter() -> PrometheusHttpAdapter { - let config = AdapterConfig::new(QueryProtocol::PrometheusHttp, QueryLanguage::promql, None); + let config = AdapterConfig::new(QueryProtocol::PrometheusHttp, QueryLanguage::PromQl, None); PrometheusHttpAdapter::new(config) } diff --git a/data_plane/src/drivers/query/adapters/victoriametrics_http.rs b/data_plane/src/drivers/query/adapters/victoriametrics_http.rs index 8c5bd433..97266c55 100644 --- a/data_plane/src/drivers/query/adapters/victoriametrics_http.rs +++ b/data_plane/src/drivers/query/adapters/victoriametrics_http.rs @@ -131,7 +131,7 @@ mod tests { fn adapter() -> VictoriaMetricsHttpAdapter { VictoriaMetricsHttpAdapter::new(AdapterConfig::new( QueryProtocol::PrometheusHttp, - QueryLanguage::promql, + QueryLanguage::PromQl, None, )) } diff --git a/data_plane/src/storage_engines/types/streaming_config.rs b/data_plane/src/storage_engines/types/streaming_config.rs index a390b8fa..91a2dc1b 100644 --- a/data_plane/src/storage_engines/types/streaming_config.rs +++ b/data_plane/src/storage_engines/types/streaming_config.rs @@ -131,7 +131,7 @@ impl StreamingConfig { let config = AggregationConfig::from_yaml_data( aggregation_data, num_aggregates_to_retain, - QueryLanguage::promql, + QueryLanguage::PromQl, )?; // PR 5: the map key IS the policy-fingerprint u64. // `AggregationConfig::policy_fp_u64()` is the canonical diff --git a/data_plane/src/tests/capability_miss_http_e2e_tests.rs b/data_plane/src/tests/capability_miss_http_e2e_tests.rs index 9d0d2632..eef74788 100644 --- a/data_plane/src/tests/capability_miss_http_e2e_tests.rs +++ b/data_plane/src/tests/capability_miss_http_e2e_tests.rs @@ -174,7 +174,7 @@ async fn start_backend(control_plane_url: String, hot_reload: HotReloadStreaming // test and stay out of the hot-vs-cold routing question. let adapter_config = AdapterConfig::new( crate::storage_engines::types::enums::QueryProtocol::PrometheusHttp, - crate::storage_engines::types::QueryLanguage::promql, + crate::storage_engines::types::QueryLanguage::PromQl, None, ); let config = HttpServerConfig {