diff --git a/src/query/src/promql/planner.rs b/src/query/src/promql/planner.rs index 45ace2501e..c043085987 100644 --- a/src/query/src/promql/planner.rs +++ b/src/query/src/promql/planner.rs @@ -2202,6 +2202,17 @@ impl PromPlanner { .context(DataFusionPlanningSnafu)?; } + ensure!( + left_context.field_columns.len() == 1, + MultiFieldsNotSupportedSnafu { + operator: "AND operator" + } + ); + // Update the field column in context. + // The AND/UNLESS operator only keep the field column in left input. + let left_field_col = left_context.field_columns.first().unwrap(); + self.ctx.field_columns = vec![left_field_col.clone()]; + // Generate join plan. // All set operations in PromQL are "distinct" match op.id() { @@ -2460,7 +2471,6 @@ impl PromPlanner { let project_fields = non_field_columns_iter .chain(field_columns_iter) .collect::>>()?; - LogicalPlanBuilder::from(input) .project(project_fields) .context(DataFusionPlanningSnafu)? @@ -3292,6 +3302,47 @@ mod test { .unwrap(); } + #[tokio::test] + async fn test_parse_and_operator() { + let mut eval_stmt = EvalStmt { + expr: PromExpr::NumberLiteral(NumberLiteral { val: 1.0 }), + start: UNIX_EPOCH, + end: UNIX_EPOCH + .checked_add(Duration::from_secs(100_000)) + .unwrap(), + interval: Duration::from_secs(5), + lookback_delta: Duration::from_secs(1), + }; + + let cases = [ + r#"count (max by (persistentvolumeclaim,namespace) (kubelet_volume_stats_used_bytes{namespace=~".+"} ) and (max by (persistentvolumeclaim,namespace) (kubelet_volume_stats_used_bytes{namespace=~".+"} )) / (max by (persistentvolumeclaim,namespace) (kubelet_volume_stats_capacity_bytes{namespace=~".+"} )) >= (80 / 100)) or vector (0)"#, + r#"count (max by (persistentvolumeclaim,namespace) (kubelet_volume_stats_used_bytes{namespace=~".+"} ) unless (max by (persistentvolumeclaim,namespace) (kubelet_volume_stats_used_bytes{namespace=~".+"} )) / (max by (persistentvolumeclaim,namespace) (kubelet_volume_stats_capacity_bytes{namespace=~".+"} )) >= (80 / 100)) or vector (0)"#, + ]; + + for case in cases { + let prom_expr = parser::parse(case).unwrap(); + eval_stmt.expr = prom_expr; + let table_provider = build_test_table_provider_with_fields( + &[ + ( + DEFAULT_SCHEMA_NAME.to_string(), + "kubelet_volume_stats_used_bytes".to_string(), + ), + ( + DEFAULT_SCHEMA_NAME.to_string(), + "kubelet_volume_stats_capacity_bytes".to_string(), + ), + ], + &["namespace", "persistentvolumeclaim"], + ) + .await; + // Should be ok + let _ = PromPlanner::stmt_to_plan(table_provider, &eval_stmt, &build_session_state()) + .await + .unwrap(); + } + } + #[tokio::test] async fn value_matcher() { // template diff --git a/tests/cases/standalone/common/promql/set_operation.result b/tests/cases/standalone/common/promql/set_operation.result index 1b15d2a59d..dcb259369c 100644 --- a/tests/cases/standalone/common/promql/set_operation.result +++ b/tests/cases/standalone/common/promql/set_operation.result @@ -465,6 +465,105 @@ drop table t2; Affected Rows: 0 +create table stats_used_bytes ( + ts timestamp time index, + namespace string, + greptime_value double, + primary key (namespace) +); + +Affected Rows: 0 + +create table stats_capacity_bytes ( + ts timestamp time index, + namespace string, + greptime_value double, + primary key (namespace) +); + +Affected Rows: 0 + +insert into stats_used_bytes values + (0, "namespace1", 1.0), + (0, "namespace2", 2.0), + (500000, "namespace1", 10.0), + (500000, "namespace2", 20.0), + (1000000, "namespace1", 25.0), + (1000000, "namespace2", 26.0); + +Affected Rows: 6 + +insert into stats_capacity_bytes values + (0, "namespace1", 30.0), + (0, "namespace2", 30.0), + (500000, "namespace1", 30.0), + (500000, "namespace2", 30.0), + (1000000, "namespace1", 30.0), + (1000000, "namespace2", 30.0); + +Affected Rows: 6 + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') max by (namespace) (stats_used_bytes{namespace=~".+"}) / max by (namespace) (stats_capacity_bytes{namespace=~".+"}) >= (80 / 100); + ++------------+---------------------+-----------------------------------------------------------------------------------------------------------------------+ +| namespace | ts | stats_used_bytes.max(stats_used_bytes.greptime_value) / stats_capacity_bytes.max(stats_capacity_bytes.greptime_value) | ++------------+---------------------+-----------------------------------------------------------------------------------------------------------------------+ +| namespace1 | 1970-01-01T00:20:00 | 0.8333333333333334 | +| namespace2 | 1970-01-01T00:20:00 | 0.8666666666666667 | ++------------+---------------------+-----------------------------------------------------------------------------------------------------------------------+ + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') max by (namespace) (stats_used_bytes{namespace=~".+"}) and (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100)); + ++------------+---------------------+--------------------------------------+ +| namespace | ts | max(stats_used_bytes.greptime_value) | ++------------+---------------------+--------------------------------------+ +| namespace1 | 1970-01-01T00:20:00 | 25.0 | +| namespace2 | 1970-01-01T00:20:00 | 26.0 | ++------------+---------------------+--------------------------------------+ + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') count(max by (namespace) (stats_used_bytes{namespace=~".+"}) and (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100))) or vector(0); + ++---------------------+---------------------------------------------+ +| ts | count(max(stats_used_bytes.greptime_value)) | ++---------------------+---------------------------------------------+ +| 1970-01-01T00:00:00 | 0 | +| 1970-01-01T00:06:40 | 0 | +| 1970-01-01T00:13:20 | 0 | +| 1970-01-01T00:20:00 | 2 | +| 1970-01-01T00:26:40 | 0 | +| 1970-01-01T00:33:20 | 0 | ++---------------------+---------------------------------------------+ + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') count(max by (namespace) (stats_used_bytes{namespace=~".+"}) and (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100))); + ++---------------------+---------------------------------------------+ +| ts | count(max(stats_used_bytes.greptime_value)) | ++---------------------+---------------------------------------------+ +| 1970-01-01T00:20:00 | 2 | ++---------------------+---------------------------------------------+ + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') count(max by (namespace) (stats_used_bytes{namespace=~".+"}) unless (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100))); + ++---------------------+---------------------------------------------+ +| ts | count(max(stats_used_bytes.greptime_value)) | ++---------------------+---------------------------------------------+ +| 1970-01-01T00:00:00 | 2 | +| 1970-01-01T00:13:20 | 2 | ++---------------------+---------------------------------------------+ + +drop table stats_used_bytes; + +Affected Rows: 0 + +drop table stats_capacity_bytes; + +Affected Rows: 0 + create table cache_hit ( ts timestamp time index, job string, diff --git a/tests/cases/standalone/common/promql/set_operation.sql b/tests/cases/standalone/common/promql/set_operation.sql index 757103142f..ff213c4512 100644 --- a/tests/cases/standalone/common/promql/set_operation.sql +++ b/tests/cases/standalone/common/promql/set_operation.sql @@ -207,6 +207,56 @@ drop table t1; drop table t2; +create table stats_used_bytes ( + ts timestamp time index, + namespace string, + greptime_value double, + primary key (namespace) +); + +create table stats_capacity_bytes ( + ts timestamp time index, + namespace string, + greptime_value double, + primary key (namespace) +); + +insert into stats_used_bytes values + (0, "namespace1", 1.0), + (0, "namespace2", 2.0), + (500000, "namespace1", 10.0), + (500000, "namespace2", 20.0), + (1000000, "namespace1", 25.0), + (1000000, "namespace2", 26.0); + +insert into stats_capacity_bytes values + (0, "namespace1", 30.0), + (0, "namespace2", 30.0), + (500000, "namespace1", 30.0), + (500000, "namespace2", 30.0), + (1000000, "namespace1", 30.0), + (1000000, "namespace2", 30.0); + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') max by (namespace) (stats_used_bytes{namespace=~".+"}) / max by (namespace) (stats_capacity_bytes{namespace=~".+"}) >= (80 / 100); + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') max by (namespace) (stats_used_bytes{namespace=~".+"}) and (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100)); + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') count(max by (namespace) (stats_used_bytes{namespace=~".+"}) and (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100))) or vector(0); + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') count(max by (namespace) (stats_used_bytes{namespace=~".+"}) and (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100))); + +-- SQLNESS SORT_RESULT 3 1 +tql eval (0, 2000, '400') count(max by (namespace) (stats_used_bytes{namespace=~".+"}) unless (max by (namespace) (stats_used_bytes{namespace=~".+"}) / (max by (namespace) (stats_capacity_bytes{namespace=~".+"})) >= (80 / 100))); + +drop table stats_used_bytes; + +drop table stats_capacity_bytes; + + create table cache_hit ( ts timestamp time index, job string,