mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-05-19 14:30:43 +00:00
feat: implement PromQL set op AND/UNLESS (#2839)
* initial impl Signed-off-by: Ruihang Xia <waynestxia@gmail.com> * disable OR for now Signed-off-by: Ruihang Xia <waynestxia@gmail.com> --------- Signed-off-by: Ruihang Xia <waynestxia@gmail.com>
This commit is contained in:
@@ -18,7 +18,7 @@ use common_error::ext::ErrorExt;
|
||||
use common_error::status_code::StatusCode;
|
||||
use common_macro::stack_trace_debug;
|
||||
use datafusion::error::DataFusionError;
|
||||
use promql_parser::parser::{Expr as PromExpr, TokenType};
|
||||
use promql_parser::parser::{Expr as PromExpr, TokenType, VectorMatchCardinality};
|
||||
use snafu::{Location, Snafu};
|
||||
|
||||
#[derive(Snafu)]
|
||||
@@ -28,6 +28,12 @@ pub enum Error {
|
||||
#[snafu(display("Unsupported expr type: {}", name))]
|
||||
UnsupportedExpr { name: String, location: Location },
|
||||
|
||||
#[snafu(display("Unsupported vector matches: {:?}", name))]
|
||||
UnsupportedVectorMatch {
|
||||
name: VectorMatchCardinality,
|
||||
location: Location,
|
||||
},
|
||||
|
||||
#[snafu(display("Unexpected token: {:?}", token))]
|
||||
UnexpectedToken {
|
||||
token: TokenType,
|
||||
@@ -112,6 +118,17 @@ pub enum Error {
|
||||
|
||||
#[snafu(display("Invalid function argument for {}", fn_name))]
|
||||
FunctionInvalidArgument { fn_name: String, location: Location },
|
||||
|
||||
#[snafu(display(
|
||||
"Attempt to combine two tables with different column sets, left: {:?}, right: {:?}",
|
||||
left,
|
||||
right
|
||||
))]
|
||||
CombineTableColumnMismatch {
|
||||
left: Vec<String>,
|
||||
right: Vec<String>,
|
||||
location: Location,
|
||||
},
|
||||
}
|
||||
|
||||
impl ErrorExt for Error {
|
||||
@@ -128,7 +145,9 @@ impl ErrorExt for Error {
|
||||
| ZeroRangeSelector { .. }
|
||||
| ColumnNotFound { .. }
|
||||
| Deserialize { .. }
|
||||
| FunctionInvalidArgument { .. } => StatusCode::InvalidArguments,
|
||||
| FunctionInvalidArgument { .. }
|
||||
| UnsupportedVectorMatch { .. }
|
||||
| CombineTableColumnMismatch { .. } => StatusCode::InvalidArguments,
|
||||
|
||||
UnknownTable { .. }
|
||||
| DataFusionPlanning { .. }
|
||||
|
||||
@@ -35,19 +35,20 @@ use datafusion::sql::TableReference;
|
||||
use datatypes::arrow::datatypes::DataType as ArrowDataType;
|
||||
use promql_parser::label::{MatchOp, Matcher, Matchers, METRIC_NAME};
|
||||
use promql_parser::parser::{
|
||||
token, AggregateExpr, BinaryExpr as PromBinaryExpr, Call, EvalStmt, Expr as PromExpr, Function,
|
||||
LabelModifier, MatrixSelector, NumberLiteral, Offset, ParenExpr, StringLiteral, SubqueryExpr,
|
||||
TokenType, UnaryExpr, VectorSelector,
|
||||
token, AggregateExpr, BinModifier, BinaryExpr as PromBinaryExpr, Call, EvalStmt,
|
||||
Expr as PromExpr, Function, LabelModifier, MatrixSelector, NumberLiteral, Offset, ParenExpr,
|
||||
StringLiteral, SubqueryExpr, TokenType, UnaryExpr, VectorMatchCardinality, VectorSelector,
|
||||
};
|
||||
use snafu::{ensure, OptionExt, ResultExt};
|
||||
use table::table::adapter::DfTableProviderAdapter;
|
||||
|
||||
use crate::error::{
|
||||
CatalogSnafu, ColumnNotFoundSnafu, DataFusionPlanningSnafu, ExpectExprSnafu,
|
||||
ExpectRangeSelectorSnafu, FunctionInvalidArgumentSnafu, MultipleMetricMatchersSnafu,
|
||||
MultipleVectorSnafu, NoMetricMatcherSnafu, Result, TableNameNotFoundSnafu,
|
||||
TimeIndexNotFoundSnafu, UnexpectedPlanExprSnafu, UnexpectedTokenSnafu, UnknownTableSnafu,
|
||||
UnsupportedExprSnafu, ValueNotFoundSnafu, ZeroRangeSelectorSnafu,
|
||||
CatalogSnafu, ColumnNotFoundSnafu, CombineTableColumnMismatchSnafu, DataFusionPlanningSnafu,
|
||||
ExpectExprSnafu, ExpectRangeSelectorSnafu, FunctionInvalidArgumentSnafu,
|
||||
MultipleMetricMatchersSnafu, MultipleVectorSnafu, NoMetricMatcherSnafu, Result,
|
||||
TableNameNotFoundSnafu, TimeIndexNotFoundSnafu, UnexpectedPlanExprSnafu, UnexpectedTokenSnafu,
|
||||
UnknownTableSnafu, UnsupportedExprSnafu, UnsupportedVectorMatchSnafu, ValueNotFoundSnafu,
|
||||
ZeroRangeSelectorSnafu,
|
||||
};
|
||||
use crate::extension_plan::{
|
||||
build_special_time_expr, EmptyMetric, HistogramFold, InstantManipulate, Millisecond,
|
||||
@@ -268,14 +269,29 @@ impl PromPlanner {
|
||||
let left_field_columns = self.ctx.field_columns.clone();
|
||||
let left_table_ref: OwnedTableReference =
|
||||
self.ctx.table_name.clone().unwrap_or_default().into();
|
||||
let left_tag_cols = self.ctx.tag_columns.clone();
|
||||
|
||||
let right_input = self.prom_expr_to_plan(*rhs.clone()).await?;
|
||||
let right_field_columns = self.ctx.field_columns.clone();
|
||||
let right_table_ref: OwnedTableReference =
|
||||
self.ctx.table_name.clone().unwrap_or_default().into();
|
||||
let right_tag_cols = self.ctx.tag_columns.clone();
|
||||
|
||||
// TODO(ruihang): avoid join if left and right are the same table
|
||||
|
||||
// set op has "special" join semantics
|
||||
if Self::is_token_a_set_op(*op) {
|
||||
return self.set_op_on_non_field_columns(
|
||||
left_input,
|
||||
right_input,
|
||||
left_tag_cols,
|
||||
right_tag_cols,
|
||||
*op,
|
||||
modifier,
|
||||
);
|
||||
}
|
||||
|
||||
// normal join
|
||||
let mut field_columns =
|
||||
left_field_columns.iter().zip(right_field_columns.iter());
|
||||
let join_plan = self.join_on_non_field_columns(
|
||||
@@ -1310,6 +1326,16 @@ impl PromPlanner {
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if the given op is a set operator (UNION, INTERSECT and EXCEPT in SQL).
|
||||
fn is_token_a_set_op(token: TokenType) -> bool {
|
||||
matches!(
|
||||
token.id(),
|
||||
token::T_LAND // INTERSECT
|
||||
| token::T_LOR // UNION
|
||||
| token::T_LUNLESS // EXCEPT
|
||||
)
|
||||
}
|
||||
|
||||
/// Build a inner join on time index column and tag columns to concat two logical plans.
|
||||
fn join_on_non_field_columns(
|
||||
&self,
|
||||
@@ -1351,6 +1377,107 @@ impl PromPlanner {
|
||||
.context(DataFusionPlanningSnafu)
|
||||
}
|
||||
|
||||
fn set_op_on_non_field_columns(
|
||||
&self,
|
||||
left: LogicalPlan,
|
||||
right: LogicalPlan,
|
||||
left_tag_cols: Vec<String>,
|
||||
right_tag_cols: Vec<String>,
|
||||
op: TokenType,
|
||||
modifier: &Option<BinModifier>,
|
||||
) -> Result<LogicalPlan> {
|
||||
let mut left_tag_col_set = left_tag_cols.into_iter().collect::<HashSet<_>>();
|
||||
let mut right_tag_col_set = right_tag_cols.into_iter().collect::<HashSet<_>>();
|
||||
|
||||
// apply modifier
|
||||
if let Some(modifier) = modifier {
|
||||
// one-to-many and many-to-one are not supported
|
||||
ensure!(
|
||||
matches!(
|
||||
modifier.card,
|
||||
VectorMatchCardinality::OneToOne | VectorMatchCardinality::ManyToMany
|
||||
),
|
||||
UnsupportedVectorMatchSnafu {
|
||||
name: modifier.card.clone(),
|
||||
},
|
||||
);
|
||||
// apply label modifier
|
||||
if let Some(matching) = &modifier.matching {
|
||||
match matching {
|
||||
// keeps columns mentioned in `on`
|
||||
LabelModifier::Include(on) => {
|
||||
let mask = on.labels.iter().cloned().collect::<HashSet<_>>();
|
||||
left_tag_col_set = left_tag_col_set.intersection(&mask).cloned().collect();
|
||||
right_tag_col_set =
|
||||
right_tag_col_set.intersection(&mask).cloned().collect();
|
||||
}
|
||||
// removes columns memtioned in `ignoring`
|
||||
LabelModifier::Exclude(ignoring) => {
|
||||
// doesn't check existence of label
|
||||
for label in &ignoring.labels {
|
||||
let _ = left_tag_col_set.remove(label);
|
||||
let _ = right_tag_col_set.remove(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ensure two sides have the same tag columns
|
||||
if !matches!(op.id(), token::T_LOR) {
|
||||
ensure!(
|
||||
left_tag_col_set == right_tag_col_set,
|
||||
CombineTableColumnMismatchSnafu {
|
||||
left: left_tag_col_set.into_iter().collect::<Vec<_>>(),
|
||||
right: right_tag_col_set.into_iter().collect::<Vec<_>>(),
|
||||
}
|
||||
)
|
||||
};
|
||||
let join_keys = left_tag_col_set
|
||||
.into_iter()
|
||||
.chain([self.ctx.time_index_column.clone().unwrap()])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Generate join plan.
|
||||
// All set operations in PromQL are "distinct"
|
||||
match op.id() {
|
||||
token::T_LAND => LogicalPlanBuilder::from(left)
|
||||
.distinct()
|
||||
.context(DataFusionPlanningSnafu)?
|
||||
.join_detailed(
|
||||
right,
|
||||
JoinType::LeftSemi,
|
||||
(join_keys.clone(), join_keys),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.context(DataFusionPlanningSnafu)?
|
||||
.build()
|
||||
.context(DataFusionPlanningSnafu),
|
||||
token::T_LUNLESS => LogicalPlanBuilder::from(left)
|
||||
.distinct()
|
||||
.context(DataFusionPlanningSnafu)?
|
||||
.join_detailed(
|
||||
right,
|
||||
JoinType::LeftAnti,
|
||||
(join_keys.clone(), join_keys),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.context(DataFusionPlanningSnafu)?
|
||||
.build()
|
||||
.context(DataFusionPlanningSnafu),
|
||||
token::T_LOR => {
|
||||
// `OR` can not be expressed by `UNION` precisely.
|
||||
// it will generate unexpceted result when schemas don't match
|
||||
UnsupportedExprSnafu {
|
||||
name: "set operation `OR`",
|
||||
}
|
||||
.fail()
|
||||
}
|
||||
_ => UnexpectedTokenSnafu { token: op }.fail(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a projection that project and perform operation expr for every value columns.
|
||||
/// Non-value columns (tag and timestamp) will be preserved in the projection.
|
||||
///
|
||||
|
||||
270
tests/cases/standalone/common/promql/set_operation.result
Normal file
270
tests/cases/standalone/common/promql/set_operation.result
Normal file
@@ -0,0 +1,270 @@
|
||||
-- from promql/testdata/operators.test
|
||||
-- cases related to AND/OR/UNLESS
|
||||
-- group_left() and group_right() are not included
|
||||
create table http_requests (
|
||||
ts timestamp time index,
|
||||
job string,
|
||||
instance string,
|
||||
g string, -- for `group`
|
||||
val double,
|
||||
primary key (job, instance, g)
|
||||
);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
insert into http_requests values
|
||||
(3000000, "api", "0", "production", 100),
|
||||
(3000000, "api", "1", "production", 200),
|
||||
(3000000, "api", "0", "canary", 300),
|
||||
(3000000, "api", "1", "canary", 400),
|
||||
(3000000, "app", "0", "production", 500),
|
||||
(3000000, "app", "1", "production", 600),
|
||||
(3000000, "app", "0", "canary", 700),
|
||||
(3000000, "app", "1", "canary", 800);
|
||||
|
||||
Affected Rows: 8
|
||||
|
||||
-- empty metric
|
||||
create table cpu_count(ts timestamp time index);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
create table vector_matching_a(
|
||||
ts timestamp time index,
|
||||
l string primary key,
|
||||
val double,
|
||||
);
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
insert into vector_matching_a values
|
||||
(3000000, "x", 10),
|
||||
(3000000, "y", 20);
|
||||
|
||||
Affected Rows: 2
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} and http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} and http_requests{instance="0"};
|
||||
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| ts | job | instance | g | val |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| 1970-01-01T00:50:00 | api | 0 | canary | 300.0 |
|
||||
| 1970-01-01T00:50:00 | app | 0 | canary | 700.0 |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and http_requests{instance="0"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and http_requests{instance="0"};
|
||||
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| job | instance | g | ts | val + Float64(1) |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| api | 0 | canary | 1970-01-01T00:50:00 | 301.0 |
|
||||
| app | 0 | canary | 1970-01-01T00:50:00 | 701.0 |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and on(instance, job) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and on(instance, job) http_requests{instance="0", g="production"};
|
||||
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| job | instance | g | ts | val + Float64(1) |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| api | 0 | canary | 1970-01-01T00:50:00 | 301.0 |
|
||||
| app | 0 | canary | 1970-01-01T00:50:00 | 701.0 |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and on(instance) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and on(instance) http_requests{instance="0", g="production"};
|
||||
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| job | instance | g | ts | val + Float64(1) |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| api | 0 | canary | 1970-01-01T00:50:00 | 301.0 |
|
||||
| app | 0 | canary | 1970-01-01T00:50:00 | 701.0 |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and ignoring(group) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and ignoring(g) http_requests{instance="0", g="production"};
|
||||
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| job | instance | g | ts | val + Float64(1) |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| api | 0 | canary | 1970-01-01T00:50:00 | 301.0 |
|
||||
| app | 0 | canary | 1970-01-01T00:50:00 | 701.0 |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and ignoring(group, job) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and ignoring(g, job) http_requests{instance="0", g="production"};
|
||||
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| job | instance | g | ts | val + Float64(1) |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
| api | 0 | canary | 1970-01-01T00:50:00 | 301.0 |
|
||||
| app | 0 | canary | 1970-01-01T00:50:00 | 701.0 |
|
||||
+-----+----------+--------+---------------------+------------------+
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} or http_requests{group="production"}
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- http_requests{group="production", instance="0", job="api-server"} 100
|
||||
-- http_requests{group="production", instance="0", job="app-server"} 500
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} or http_requests{g="production"};
|
||||
|
||||
Error: 1004(InvalidArguments), Unsupported expr type: set operation `OR`
|
||||
|
||||
-- # On overlap the rhs samples must be dropped.
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) or http_requests{instance="1"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- {group="canary", instance="1", job="api-server"} 401
|
||||
-- {group="canary", instance="1", job="app-server"} 801
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or http_requests{instance="1"};
|
||||
|
||||
Error: 1004(InvalidArguments), Unsupported expr type: set operation `OR`
|
||||
|
||||
-- # Matching only on instance excludes everything that has instance=0/1 but includes
|
||||
-- # entries without the instance label.
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a)
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- {group="canary", instance="1", job="api-server"} 401
|
||||
-- {group="canary", instance="1", job="app-server"} 801
|
||||
-- vector_matching_a{l="x"} 10
|
||||
-- vector_matching_a{l="y"} 20
|
||||
-- NOT SUPPORTED: union on different schemas
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a);
|
||||
|
||||
Error: 1004(InvalidArguments), Unsupported expr type: set operation `OR`
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) or ignoring(l, group, job) (http_requests or cpu_count or vector_matching_a)
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- {group="canary", instance="1", job="api-server"} 401
|
||||
-- {group="canary", instance="1", job="app-server"} 801
|
||||
-- vector_matching_a{l="x"} 10
|
||||
-- vector_matching_a{l="y"} 20
|
||||
-- NOT SUPPORTED: union on different schemas
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or ignoring(l, g, job) (http_requests or cpu_count or vector_matching_a);
|
||||
|
||||
Error: 1004(InvalidArguments), Unsupported expr type: set operation `OR`
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless http_requests{instance="0"};
|
||||
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| ts | job | instance | g | val |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| 1970-01-01T00:50:00 | api | 1 | canary | 400.0 |
|
||||
| 1970-01-01T00:50:00 | app | 1 | canary | 800.0 |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless on(job) http_requests{instance="0"}
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless on(job) http_requests{instance="0"};
|
||||
|
||||
++
|
||||
++
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless on(job, instance) http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless on(job, instance) http_requests{instance="0"};
|
||||
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| ts | job | instance | g | val |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| 1970-01-01T00:50:00 | api | 1 | canary | 400.0 |
|
||||
| 1970-01-01T00:50:00 | app | 1 | canary | 800.0 |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless ignoring(group, instance) http_requests{instance="0"}
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless ignoring(g, instance) http_requests{instance="0"};
|
||||
|
||||
++
|
||||
++
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless ignoring(group) http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless ignoring(g) http_requests{instance="0"};
|
||||
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| ts | job | instance | g | val |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
| 1970-01-01T00:50:00 | api | 1 | canary | 400.0 |
|
||||
| 1970-01-01T00:50:00 | app | 1 | canary | 800.0 |
|
||||
+---------------------+-----+----------+--------+-------+
|
||||
|
||||
-- # https://github.com/prometheus/prometheus/issues/1489
|
||||
-- eval instant at 50m http_requests AND ON (dummy) vector(1)
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- http_requests{group="production", instance="0", job="api-server"} 100
|
||||
-- http_requests{group="production", instance="0", job="app-server"} 500
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `vector()`
|
||||
tql eval (3000, 3000, '1s') http_requests AND ON (dummy) vector(1);
|
||||
|
||||
Error: 1004(InvalidArguments), Expect a PromQL expr but not found, input expr: Call(Call { func: Function { name: "vector", arg_types: [Scalar], variadic: false, return_type: Vector }, args: FunctionArgs { args: [NumberLiteral(NumberLiteral { val: 1.0 })] } })
|
||||
|
||||
-- eval instant at 50m http_requests AND IGNORING (group, instance, job) vector(1)
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- http_requests{group="production", instance="0", job="api-server"} 100
|
||||
-- http_requests{group="production", instance="0", job="app-server"} 500
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `vector()`
|
||||
tql eval (3000, 3000, '1s') http_requests AND IGNORING (g, instance, job) vector(1);
|
||||
|
||||
Error: 1004(InvalidArguments), Expect a PromQL expr but not found, input expr: Call(Call { func: Function { name: "vector", arg_types: [Scalar], variadic: false, return_type: Vector }, args: FunctionArgs { args: [NumberLiteral(NumberLiteral { val: 1.0 })] } })
|
||||
|
||||
drop table http_requests;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
drop table cpu_count;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
drop table vector_matching_a;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
175
tests/cases/standalone/common/promql/set_operation.sql
Normal file
175
tests/cases/standalone/common/promql/set_operation.sql
Normal file
@@ -0,0 +1,175 @@
|
||||
-- from promql/testdata/operators.test
|
||||
-- cases related to AND/OR/UNLESS
|
||||
-- group_left() and group_right() are not included
|
||||
|
||||
create table http_requests (
|
||||
ts timestamp time index,
|
||||
job string,
|
||||
instance string,
|
||||
g string, -- for `group`
|
||||
val double,
|
||||
primary key (job, instance, g)
|
||||
);
|
||||
|
||||
insert into http_requests values
|
||||
(3000000, "api", "0", "production", 100),
|
||||
(3000000, "api", "1", "production", 200),
|
||||
(3000000, "api", "0", "canary", 300),
|
||||
(3000000, "api", "1", "canary", 400),
|
||||
(3000000, "app", "0", "production", 500),
|
||||
(3000000, "app", "1", "production", 600),
|
||||
(3000000, "app", "0", "canary", 700),
|
||||
(3000000, "app", "1", "canary", 800);
|
||||
|
||||
-- empty metric
|
||||
create table cpu_count(ts timestamp time index);
|
||||
|
||||
create table vector_matching_a(
|
||||
ts timestamp time index,
|
||||
l string primary key,
|
||||
val double,
|
||||
);
|
||||
|
||||
insert into vector_matching_a values
|
||||
(3000000, "x", 10),
|
||||
(3000000, "y", 20);
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} and http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} and http_requests{instance="0"};
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and http_requests{instance="0"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and http_requests{instance="0"};
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and on(instance, job) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and on(instance, job) http_requests{instance="0", g="production"};
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and on(instance) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and on(instance) http_requests{instance="0", g="production"};
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and ignoring(group) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and ignoring(g) http_requests{instance="0", g="production"};
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) and ignoring(group, job) http_requests{instance="0", group="production"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and ignoring(g, job) http_requests{instance="0", g="production"};
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} or http_requests{group="production"}
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- http_requests{group="production", instance="0", job="api-server"} 100
|
||||
-- http_requests{group="production", instance="0", job="app-server"} 500
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} or http_requests{g="production"};
|
||||
|
||||
-- # On overlap the rhs samples must be dropped.
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) or http_requests{instance="1"}
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- {group="canary", instance="1", job="api-server"} 401
|
||||
-- {group="canary", instance="1", job="app-server"} 801
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or http_requests{instance="1"};
|
||||
|
||||
|
||||
-- # Matching only on instance excludes everything that has instance=0/1 but includes
|
||||
-- # entries without the instance label.
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a)
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- {group="canary", instance="1", job="api-server"} 401
|
||||
-- {group="canary", instance="1", job="app-server"} 801
|
||||
-- vector_matching_a{l="x"} 10
|
||||
-- vector_matching_a{l="y"} 20
|
||||
-- NOT SUPPORTED: union on different schemas
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or on(instance) (http_requests or cpu_count or vector_matching_a);
|
||||
|
||||
-- eval instant at 50m (http_requests{group="canary"} + 1) or ignoring(l, group, job) (http_requests or cpu_count or vector_matching_a)
|
||||
-- {group="canary", instance="0", job="api-server"} 301
|
||||
-- {group="canary", instance="0", job="app-server"} 701
|
||||
-- {group="canary", instance="1", job="api-server"} 401
|
||||
-- {group="canary", instance="1", job="app-server"} 801
|
||||
-- vector_matching_a{l="x"} 10
|
||||
-- vector_matching_a{l="y"} 20
|
||||
-- NOT SUPPORTED: union on different schemas
|
||||
-- NOT SUPPORTED: `or`
|
||||
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or ignoring(l, g, job) (http_requests or cpu_count or vector_matching_a);
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless http_requests{instance="0"};
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless on(job) http_requests{instance="0"}
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless on(job) http_requests{instance="0"};
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless on(job, instance) http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless on(job, instance) http_requests{instance="0"};
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless ignoring(group, instance) http_requests{instance="0"}
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless ignoring(g, instance) http_requests{instance="0"};
|
||||
|
||||
-- eval instant at 50m http_requests{group="canary"} unless ignoring(group) http_requests{instance="0"}
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- SQLNESS SORT_RESULT 3 1
|
||||
tql eval (3000, 3000, '1s') http_requests{g="canary"} unless ignoring(g) http_requests{instance="0"};
|
||||
|
||||
|
||||
-- # https://github.com/prometheus/prometheus/issues/1489
|
||||
-- eval instant at 50m http_requests AND ON (dummy) vector(1)
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- http_requests{group="production", instance="0", job="api-server"} 100
|
||||
-- http_requests{group="production", instance="0", job="app-server"} 500
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `vector()`
|
||||
tql eval (3000, 3000, '1s') http_requests AND ON (dummy) vector(1);
|
||||
|
||||
-- eval instant at 50m http_requests AND IGNORING (group, instance, job) vector(1)
|
||||
-- http_requests{group="canary", instance="0", job="api-server"} 300
|
||||
-- http_requests{group="canary", instance="0", job="app-server"} 700
|
||||
-- http_requests{group="canary", instance="1", job="api-server"} 400
|
||||
-- http_requests{group="canary", instance="1", job="app-server"} 800
|
||||
-- http_requests{group="production", instance="0", job="api-server"} 100
|
||||
-- http_requests{group="production", instance="0", job="app-server"} 500
|
||||
-- http_requests{group="production", instance="1", job="api-server"} 200
|
||||
-- http_requests{group="production", instance="1", job="app-server"} 600
|
||||
-- NOT SUPPORTED: `vector()`
|
||||
tql eval (3000, 3000, '1s') http_requests AND IGNORING (g, instance, job) vector(1);
|
||||
|
||||
drop table http_requests;
|
||||
|
||||
drop table cpu_count;
|
||||
|
||||
drop table vector_matching_a;
|
||||
Reference in New Issue
Block a user