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
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ mod tests {
if let Some(query_data) = SQLPatternParser::new(&schema, time).parse_query(&statements) {
let result = matcher.query_info_to_pattern(&query_data);
assert!(result.is_valid());
assert_eq!(result.query_type, vec![QueryType::Spatial]);
assert_eq!(result.query_type, vec![QueryType::SpatioTemporal]);
}
}

Expand All @@ -109,7 +109,7 @@ mod tests {
fn test_dated_temporal_sum() {
check_query(
"SELECT SUM(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalGeneric],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -118,7 +118,7 @@ mod tests {
fn test_dated_temporal_quantile() {
check_query(
"SELECT QUANTILE(0.95, value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -127,7 +127,7 @@ mod tests {
fn test_dated_spatial_avg() {
check_query(
"SELECT AVG(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' GROUP BY L1, L2, L3, L4",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -136,7 +136,7 @@ mod tests {
fn test_dated_spatial_quantile() {
check_query(
"SELECT QUANTILE(0.95, value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -156,7 +156,7 @@ mod tests {
fn test_temporal_quantile() {
check_query(
"SELECT QUANTILE(0.95, value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -165,7 +165,7 @@ mod tests {
fn test_temporal_sum() {
check_query(
"SELECT SUM(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalGeneric],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -174,7 +174,7 @@ mod tests {
fn test_temporal_max() {
check_query(
"SELECT MAX(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalGeneric],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -183,7 +183,7 @@ mod tests {
fn test_temporal_min() {
check_query(
"SELECT MIN(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalGeneric],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -192,7 +192,7 @@ mod tests {
fn test_temporal_avg() {
check_query(
"SELECT AVG(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalGeneric],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -203,7 +203,7 @@ mod tests {
fn test_spatial_sum() {
check_query(
"SELECT SUM(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -212,7 +212,7 @@ mod tests {
fn test_spatial_max() {
check_query(
"SELECT MAX(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1, L2",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -221,7 +221,7 @@ mod tests {
fn test_spatial_min() {
check_query(
"SELECT MIN(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1, L2, L3",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -230,7 +230,7 @@ mod tests {
fn test_spatial_avg() {
check_query(
"SELECT AVG(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -239,7 +239,7 @@ mod tests {
fn test_spatial_quantile() {
check_query(
"SELECT QUANTILE(0.95, value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand Down Expand Up @@ -425,7 +425,7 @@ mod tests {
fn test_temporal_percentile() {
check_query(
"SELECT PERCENTILE(value, 95) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -434,7 +434,7 @@ mod tests {
fn test_spatial_percentile() {
check_query(
"SELECT PERCENTILE(value, 95) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand Down Expand Up @@ -464,7 +464,7 @@ mod tests {
fn test_clickhouse_temporal_quantile() {
check_query(
"SELECT quantile(0.95)(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -473,7 +473,7 @@ mod tests {
fn test_clickhouse_spatial_quantile() {
check_query(
"SELECT quantile(0.95)(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -1, NOW()) AND NOW() GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand Down Expand Up @@ -504,7 +504,7 @@ mod tests {
fn test_clickhouse_explicit_datetime_temporal_quantile() {
check_query(
"SELECT quantile(0.95)(value) FROM cpu_usage WHERE time BETWEEN '2025-10-01 00:00:00' AND '2025-10-01 00:00:10' GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -516,7 +516,7 @@ mod tests {
fn test_asap_only_iso_z_temporal_quantile() {
check_query(
"SELECT quantile(0.95)(value) FROM cpu_usage WHERE time BETWEEN '2025-10-01T00:00:00Z' AND '2025-10-01T00:00:10Z' GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -528,7 +528,7 @@ mod tests {
fn test_iso_no_z_treated_as_local_time_temporal_quantile() {
check_query(
"SELECT quantile(0.95)(value) FROM cpu_usage WHERE time BETWEEN '2025-10-01T00:00:00' AND '2025-10-01T00:00:10' GROUP BY L1, L2, L3, L4",
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -537,7 +537,7 @@ mod tests {
fn test_clickhouse_explicit_datetime_spatial_quantile() {
check_query(
"SELECT quantile(0.95)(value) FROM cpu_usage WHERE time BETWEEN '2025-10-01 00:00:00' AND '2025-10-01 00:00:01' GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand Down Expand Up @@ -738,7 +738,7 @@ mod tests {
WHERE time BETWEEN DATEADD(s, -15, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' \
GROUP BY L1, L2, L3, L4",
15.0,
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand All @@ -751,20 +751,20 @@ mod tests {
WHERE time BETWEEN DATEADD(s, -30, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' \
GROUP BY L1, L2, L3, L4",
15.0,
vec![QueryType::TemporalGeneric],
vec![QueryType::SpatioTemporal],
None,
);
}

/// scraped_intervals = 30/15 = 2.0 with QUANTILE agg → TemporalQuantile
/// scraped_intervals = 30/15 = 2.0 with QUANTILE agg → SpatioTemporal
#[test]
fn test_bug_201_temporal_quantile_not_rejected() {
check_query_with_interval(
"SELECT QUANTILE(0.95, value) FROM cpu_usage \
WHERE time BETWEEN DATEADD(s, -30, '2025-10-01 00:00:00') AND '2025-10-01 00:00:00' \
GROUP BY L1, L2, L3, L4",
15.0,
vec![QueryType::TemporalQuantile],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand Down Expand Up @@ -1305,7 +1305,7 @@ mod tests {
"SELECT SUM(value) FROM cpu_usage \
WHERE time >= DATEADD(s, -1, NOW()) AND time < NOW() \
GROUP BY L1",
vec![QueryType::Spatial],
vec![QueryType::SpatioTemporal],
None,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ use crate::sqlhelper::TimeInfo;

use std::collections::HashSet;

/// Every valid SQL query classifies as SpatioTemporal — query-type-agnostic,
/// so this is a single-variant enum. Kept (rather than removed outright) so
/// `SQLQuery.query_type: Vec<QueryType>` doesn't need reshaping here — see
/// #512 for that follow-up.
#[derive(Debug, Clone, PartialEq)]
pub enum QueryType {
Spatial,
TemporalGeneric,
TemporalQuantile,
SpatioTemporal,
}

Expand All @@ -22,6 +23,7 @@ pub enum QueryError {
IllegalAggregationFn,
SpatialDurationSmall,
NestedQueryUnsupported,
MissingTimeRange,
}

#[derive(Debug)]
Expand Down Expand Up @@ -262,52 +264,32 @@ impl SQLPatternMatcher {
if let Some((metric, aggregation_info, scrape_duration, labels, time_info)) =
query_data.first()
{
if (scrape_duration - 1.0).abs() < f64::EPSILON {
sql_query.add_subquery(
QueryType::Spatial,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
// scrape_duration is 0.0 only for the UNUSED-time-column marker
// (the outer layer of a nested query, which the len() > 1 check
// above already rejects) — not reachable for a flat query, since
// any real time range short enough to compute a 0.0 (or < 1
// scrape interval) duration already errors out above via
// SpatialDurationSmall. Kept as an explicit error rather than
// silently leaving the query unmatched, in case that invariant
// ever changes.
if *scrape_duration == 0.0 {
println!("Returned QueryError::MissingTimeRange");

return SQLQuery::new(
Vec::new(),
Some(QueryError::MissingTimeRange),
Some("query has no resolvable time range".to_string()),
);
} else if *scrape_duration > 1.0 {
// Check if labels match all metadata columns
let has_all_labels = self
.schema
.get_metadata_columns(metric)
.map(|schema_metadata_columns| labels == schema_metadata_columns)
.unwrap_or(true);

if has_all_labels {
// Full temporal query with all labels (PromQL-equivalent)
if aggregation_info.get_name() == "QUANTILE" {
sql_query.add_subquery(
QueryType::TemporalQuantile,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
);
} else {
sql_query.add_subquery(
QueryType::TemporalGeneric,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
);
}
} else {
// SpatioTemporal: spans multiple scrape intervals but groups by subset of labels
sql_query.add_subquery(
QueryType::SpatioTemporal,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
);
}
}

// Every valid query classifies as SpatioTemporal
sql_query.add_subquery(
QueryType::SpatioTemporal,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
);
}

sql_query
Expand Down
Loading
Loading