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 @@ -145,8 +145,8 @@ mod tests {
fn test_dated_spatial_of_temporal_quantile_max() {
check_query(
"SELECT QUANTILE(0.95, value) FROM (SELECT MAX(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) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

Expand Down Expand Up @@ -250,125 +250,125 @@ mod tests {
fn test_spatial_of_temporal_sum_sum() {
check_query(
"SELECT SUM(result) FROM (SELECT SUM(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_sum_min() {
check_query(
"SELECT SUM(result) FROM (SELECT MIN(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_sum_max() {
check_query(
"SELECT SUM(result) FROM (SELECT MAX(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2, L3",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_sum_avg() {
check_query(
"SELECT SUM(result) FROM (SELECT AVG(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2, L3, L4",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_max_sum() {
check_query(
"SELECT MAX(result) FROM (SELECT SUM(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_max_min() {
check_query(
"SELECT MAX(result) FROM (SELECT MIN(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_max_max() {
check_query(
"SELECT MAX(result) FROM (SELECT MAX(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2, L3",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_max_avg() {
check_query(
"SELECT MAX(result) FROM (SELECT AVG(value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2, L3, L4",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_quantile_max() {
check_query(
"SELECT QUANTILE(0.95, value) FROM (SELECT MAX(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_quantile_min() {
check_query(
"SELECT QUANTILE(0.95, value) FROM (SELECT MIN(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_quantile_sum() {
check_query(
"SELECT QUANTILE(0.95, value) FROM (SELECT SUM(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_quantile_avg() {
check_query(
"SELECT QUANTILE(0.95, value) FROM (SELECT AVG(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_avg_quantile() {
check_query(
"SELECT AVG(result) FROM (SELECT QUANTILE(0.95, value) AS result FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2",
vec![QueryType::Spatial, QueryType::TemporalQuantile],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

#[test]
fn test_spatial_of_temporal_quantile_quantile() {
check_query(
"SELECT QUANTILE(0.95, value) FROM (SELECT QUANTILE(0.95, value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1, L2, L3",
vec![QueryType::Spatial, QueryType::TemporalQuantile],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

Expand Down Expand Up @@ -452,8 +452,8 @@ mod tests {
fn test_spatial_of_temporal_percentile_max() {
check_query(
"SELECT PERCENTILE(value, 95) FROM (SELECT MAX(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

Expand Down Expand Up @@ -491,8 +491,8 @@ mod tests {
fn test_clickhouse_spatial_of_temporal_quantile_max() {
check_query(
"SELECT quantile(0.95)(value) FROM (SELECT MAX(value) FROM cpu_usage WHERE time BETWEEN DATEADD(s, -10, NOW()) AND NOW() GROUP BY L1, L2, L3, L4) GROUP BY L1",
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

Expand Down Expand Up @@ -782,18 +782,19 @@ mod tests {
);
}

/// Spatial-of-temporal: outer has UNUSED time (not checked), inner scraped_intervals = 30/15 = 2.0
/// Nested query with a fine inner duration (30/15 = 2.0, so the bug-201 guard
/// wouldn't false-positive) still gets rejected as unsupported, not matched.
#[test]
fn test_bug_201_spatial_of_temporal_not_rejected() {
fn test_bug_201_spatial_of_temporal_rejected_as_nested() {
check_query_with_interval(
"SELECT SUM(result) FROM \
(SELECT SUM(value) AS result 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) \
GROUP BY L1",
15.0,
vec![QueryType::Spatial, QueryType::TemporalGeneric],
None,
vec![],
Some(QueryError::NestedQueryUnsupported),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum QueryError {
TemporalMissingLabels, // indistinguishable from too large scrape duration
IllegalAggregationFn,
SpatialDurationSmall,
NestedQueryUnsupported,
}

#[derive(Debug)]
Expand Down Expand Up @@ -243,70 +244,68 @@ impl SQLPatternMatcher {
};
println!("flattened QueryData: {query_data:?}");

if query_data.len() > 1 {
println!("Returned QueryError::NestedQueryUnsupported");

return SQLQuery::new(
Vec::new(),
Some(QueryError::NestedQueryUnsupported),
Some(format!(
"nested SQL queries are not supported (n={})",
query_data.len()
)),
);
}

let mut sql_query = SQLQuery::new(Vec::new(), None, None);

for (i, (metric, aggregation_info, scrape_duration, labels, time_info)) in
query_data.iter().enumerate()
if let Some((metric, aggregation_info, scrape_duration, labels, time_info)) =
query_data.first()
{
if i < query_data.len() - 1 {
// Not the last query
// let time_info = TimeInfo::new("time".to_string(), *start, *scrape_duration); // You may need to adjust this
if (scrape_duration - 1.0).abs() < f64::EPSILON {
sql_query.add_subquery(
QueryType::Spatial,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
);
} else {
// Last query
// let time_info = TimeInfo::new("time".to_string(), *start, *scrape_duration);

if (scrape_duration - 1.0).abs() < f64::EPSILON {
sql_query.add_subquery(
QueryType::Spatial,
aggregation_info.clone(),
metric.clone(),
labels.clone(),
time_info.clone(),
);
} 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 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 {
// SpatioTemporal: spans multiple scrape intervals but groups by subset of labels
sql_query.add_subquery(
QueryType::SpatioTemporal,
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(),
);
}
}
}
Expand Down
Loading
Loading