fix(promql): resolve dotted column names as unqualified columns (#9391)

* fix(promql): resolve dotted column names as unqualified columns

col(), From<&str>/From<String> for Column and string join keys go through
Column::from_qualified_name, which splits `service.name` into relation
`service` and column `name` and lowercases unquoted identifiers. Build
PromQL column references with Column::from_name / ident() instead.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(servers): resolve remote read matcher labels as unqualified columns

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* test(promql): cover same-name columns differing in case and without()

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
This commit is contained in:
dennis zhuang
2026-09-29 08:13:28 +00:00
committed by GitHub
parent abf1396c28
commit 218000e21b
16 changed files with 728 additions and 64 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ use datafusion::physical_plan::{
Partitioning, PhysicalExpr, PlanProperties, RecordBatchStream, SendableRecordBatchStream,
};
use datafusion_common::DFSchema;
use datafusion_expr::{EmptyRelation, col};
use datafusion_expr::{EmptyRelation, ident};
use datatypes::arrow;
use datatypes::arrow::array::{ArrayRef, Float64Array, TimestampMillisecondArray};
use datatypes::arrow::datatypes::{DataType, Field, SchemaRef, TimeUnit};
@@ -108,7 +108,7 @@ impl UserDefinedLogicalNodeCore for Absent {
return vec![];
}
vec![col(&self.time_index_column)]
vec![ident(&self.time_index_column)]
}
fn necessary_children_exprs(&self, _output_columns: &[usize]) -> Option<Vec<Vec<usize>>> {
@@ -40,7 +40,7 @@ use datafusion::physical_plan::{
SendableRecordBatchStream, StatisticsArgs,
};
use datafusion::physical_planner::PhysicalPlanner;
use datafusion::prelude::{Expr, col, lit};
use datafusion::prelude::{Expr, ident, lit};
use datafusion_expr::LogicalPlanBuilder;
use datatypes::arrow::array::TimestampMillisecondArray;
use datatypes::arrow::datatypes::SchemaRef;
@@ -409,7 +409,7 @@ fn build_ts_only_schema(column_name: &str) -> DFSchema {
pub fn build_special_time_expr(time_index_column_name: &str) -> Expr {
let input_schema = build_ts_only_schema(time_index_column_name);
// safety: should not failed (UT covers this)
col(time_index_column_name)
ident(time_index_column_name)
.cast_to(&DataType::Int64, &input_schema)
.unwrap()
.cast_to(&DataType::Float64, &input_schema)
@@ -41,7 +41,7 @@ use datafusion::physical_plan::{
SendableRecordBatchStream, StatisticsArgs,
};
use datafusion::prelude::{Column, Expr};
use datafusion_expr::{EmptyRelation, col};
use datafusion_expr::{EmptyRelation, ident};
use datatypes::arrow_array::string_array_value_at_index;
use datatypes::prelude::{ConcreteDataType, DataType as GtDataType};
use datatypes::value::{OrderedF64, Value, ValueRef};
@@ -141,14 +141,14 @@ impl UserDefinedLogicalNodeCore for HistogramFold {
}
let mut exprs = vec![
col(&self.le_column),
col(&self.ts_column),
col(&self.field_column),
ident(&self.le_column),
ident(&self.ts_column),
ident(&self.field_column),
];
exprs.extend(self.input.schema().fields().iter().filter_map(|f| {
let name = f.name();
if name != &self.le_column && name != &self.ts_column && name != &self.field_column {
Some(col(name))
Some(ident(name))
} else {
None
}
@@ -37,7 +37,7 @@ use datafusion::physical_plan::{
PhysicalExpr, PlanProperties, RecordBatchStream, SendableRecordBatchStream, Statistics,
StatisticsArgs,
};
use datafusion_expr::col;
use datafusion_expr::ident;
use datatypes::arrow::compute;
use datatypes::timestamp::timestamp_array_to_primitive;
use futures::{Stream, StreamExt, ready};
@@ -140,8 +140,8 @@ impl UserDefinedLogicalNodeCore for InstantManipulate {
return vec![];
}
let mut exprs = vec![col(&self.time_index_column)];
exprs.extend(self.staleness_field_columns().map(col));
let mut exprs = vec![ident(&self.time_index_column)];
exprs.extend(self.staleness_field_columns().map(ident));
exprs
}
+3 -3
View File
@@ -33,7 +33,7 @@ use datafusion::physical_plan::{
InputDistributionRequirements, PhysicalExpr, PlanProperties, RecordBatchStream,
SendableRecordBatchStream, StatisticsArgs,
};
use datafusion_expr::col;
use datafusion_expr::ident;
use datatypes::arrow::array::TimestampMillisecondArray;
use datatypes::arrow::datatypes::{SchemaRef, TimestampMillisecondType};
use datatypes::arrow::record_batch::RecordBatch;
@@ -93,8 +93,8 @@ impl UserDefinedLogicalNodeCore for SeriesNormalize {
self.tag_columns
.iter()
.map(col)
.chain(std::iter::once(col(&self.time_index_column_name)))
.map(ident)
.chain(std::iter::once(ident(&self.time_index_column_name)))
.collect()
}
@@ -38,7 +38,7 @@ use datafusion::physical_plan::{
InputDistributionRequirements, PhysicalExpr, PlanProperties, RecordBatchStream,
SendableRecordBatchStream, Statistics, StatisticsArgs,
};
use datafusion_expr::col;
use datafusion_expr::ident;
use datatypes::timestamp::timestamp_array_to_primitive;
use futures::{Stream, StreamExt, ready};
use greptime_proto::substrait_extension as pb;
@@ -318,8 +318,8 @@ impl UserDefinedLogicalNodeCore for RangeManipulate {
}
let mut exprs = Vec::with_capacity(1 + self.field_columns.len());
exprs.push(col(&self.time_index));
exprs.extend(self.field_columns.iter().map(col));
exprs.push(ident(&self.time_index));
exprs.extend(self.field_columns.iter().map(ident));
exprs
}
@@ -33,7 +33,7 @@ use datafusion::physical_plan::{
SendableRecordBatchStream, StatisticsArgs,
};
use datafusion::prelude::Expr;
use datafusion_expr::col;
use datafusion_expr::ident;
use datatypes::arrow::array::{Array, ArrayRef, Float64Array, TimestampMillisecondArray};
use datatypes::arrow::compute::{CastOptions, cast_with_options, concat_batches};
use datatypes::arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit};
@@ -282,9 +282,9 @@ impl UserDefinedLogicalNodeCore for ScalarCalculate {
self.tag_columns
.iter()
.map(col)
.chain(std::iter::once(col(&self.time_index)))
.chain(std::iter::once(col(&self.field_column)))
.map(ident)
.chain(std::iter::once(ident(&self.time_index)))
.chain(std::iter::once(ident(&self.field_column)))
.collect()
}
@@ -38,7 +38,7 @@ use datafusion::physical_plan::{
DisplayAs, DisplayFormatType, Distribution, ExecutionPlan, InputDistributionRequirements,
PhysicalExpr, PlanProperties, RecordBatchStream, SendableRecordBatchStream,
};
use datafusion_expr::col;
use datafusion_expr::ident;
use datatypes::arrow::compute;
use datatypes::arrow_array::string_array_value_at_index;
use datatypes::compute::SortOptions;
@@ -186,8 +186,8 @@ impl UserDefinedLogicalNodeCore for SeriesDivide {
self.tag_columns
.iter()
.map(col)
.chain(std::iter::once(col(&self.time_index_column)))
.map(ident)
.chain(std::iter::once(ident(&self.time_index_column)))
.collect()
}
@@ -34,7 +34,7 @@ use datafusion::physical_plan::{
Partitioning, PhysicalExpr, PlanProperties, RecordBatchStream, SendableRecordBatchStream,
hash_utils,
};
use datafusion_expr::col;
use datafusion_expr::ident;
use datatypes::arrow::compute;
use futures::{Stream, StreamExt, ready};
use greptime_proto::substrait_extension as pb;
@@ -278,12 +278,12 @@ impl UserDefinedLogicalNodeCore for UnionDistinctOn {
let mut exprs = self
.compare_key_indices
.iter()
.filter_map(|index| fields.get(*index).map(|field| col(field.name())))
.filter_map(|index| fields.get(*index).map(|field| ident(field.name())))
.collect::<Vec<_>>();
if !self.compare_key_indices.contains(&self.ts_col_idx)
&& let Some(field) = fields.get(self.ts_col_idx)
{
exprs.push(col(field.name()));
exprs.push(ident(field.name()));
}
exprs
}
+20 -18
View File
@@ -58,7 +58,7 @@ use datafusion_common::{DFSchema, NullEquality, TableReference};
use datafusion_expr::expr::WindowFunctionParams;
use datafusion_expr::expr_fn::when;
use datafusion_expr::utils::{conjunction, disjunction};
use datafusion_expr::{ExprSchemable, Literal, SortExpr, TableSource, col, lit};
use datafusion_expr::{ExprSchemable, Literal, SortExpr, TableSource, ident, lit};
use datafusion_functions::core::coalesce;
use datatypes::arrow::datatypes::{DataType as ArrowDataType, TimeUnit as ArrowTimeUnit};
use datatypes::data_type::{ConcreteDataType, DataType as GreptimeDataType};
@@ -678,7 +678,7 @@ impl PromPlanner {
.cloned()
.chain(prev_field_exprs.clone())
.collect::<Vec<_>>();
group_exprs.push(col(label));
group_exprs.push(ident(label));
let project_fields = self
.create_field_column_exprs()?
.into_iter()
@@ -798,10 +798,10 @@ impl PromPlanner {
),
self.promql_annotations.clone(),
)),
args: vec![col(&histogram_column)],
args: vec![ident(&histogram_column)],
});
let keep_float = when(col(&histogram_column).is_not_null(), drop_histogram)
.otherwise(col(&float_column).is_not_null())
let keep_float = when(ident(&histogram_column).is_not_null(), drop_histogram)
.otherwise(ident(&float_column).is_not_null())
.context(DataFusionPlanningSnafu)?;
input = LogicalPlanBuilder::from(input)
.filter(keep_float)
@@ -852,7 +852,7 @@ impl PromPlanner {
.iter()
.fold(None, |expr, rank| {
let predicate = DfExpr::BinaryExpr(BinaryExpr {
left: Box::new(col(rank)),
left: Box::new(ident(rank)),
op: Operator::LtEq,
right: Box::new(val.clone()),
});
@@ -868,7 +868,7 @@ impl PromPlanner {
})
.unwrap();
let rank_columns: Vec<_> = rank_columns.into_iter().map(col).collect();
let rank_columns: Vec<_> = rank_columns.into_iter().map(ident).collect();
let mut new_group_exprs = group_exprs.clone();
// Order by ranks
@@ -921,10 +921,12 @@ impl PromPlanner {
if Self::field_column_is_native_histogram(&input_schema, col) {
Ok(DfExpr::ScalarFunction(ScalarFunction {
func: Arc::new(NativeHistogramNeg::scalar_udf()),
args: vec![DfExpr::Column(col.into())],
args: vec![DfExpr::Column(Column::from_name(col))],
}))
} else {
Ok(DfExpr::Negative(Box::new(DfExpr::Column(col.into()))))
Ok(DfExpr::Negative(Box::new(DfExpr::Column(
Column::from_name(col),
))))
}
})
}
@@ -1034,7 +1036,7 @@ impl PromPlanner {
let binary_expr_builder = Self::prom_token_to_binary_expr_builder(*op)?;
let rhs_is_histogram =
Self::field_column_is_native_histogram(&input_schema, col);
let rhs = DfExpr::Column(col.into());
let rhs = DfExpr::Column(Column::from_name(col));
let mut binary_expr = match Self::native_histogram_binary_expr(
*op,
expr.clone(),
@@ -1101,7 +1103,7 @@ impl PromPlanner {
let binary_expr_builder = Self::prom_token_to_binary_expr_builder(*op)?;
let lhs_is_histogram =
Self::field_column_is_native_histogram(&input_schema, col);
let lhs = DfExpr::Column(col.into());
let lhs = DfExpr::Column(Column::from_name(col));
let mut binary_expr = match Self::native_histogram_binary_expr(
*op,
lhs.clone(),
@@ -1753,7 +1755,7 @@ impl PromPlanner {
// `timestamp()` preserves the shifted selector timeline even though
// SeriesNormalize now retains raw native timestamp storage. Decimal
// arithmetic shifts before truncating to milliseconds.
let unit_factor = match col(&time_index_column)
let unit_factor = match ident(&time_index_column)
.get_type(normalize.schema())
.context(DataFusionPlanningSnafu)?
{
@@ -1763,7 +1765,7 @@ impl PromPlanner {
ArrowDataType::Timestamp(ArrowTimeUnit::Nanosecond, _) => (1, 7, 6),
_ => unreachable!("time index is a timestamp"),
};
let sample_time = col(&time_index_column)
let sample_time = ident(&time_index_column)
.cast_to(&ArrowDataType::Int64, normalize.schema())
.context(DataFusionPlanningSnafu)?
.cast_to(&ArrowDataType::Decimal128(19, 0), normalize.schema())
@@ -1877,7 +1879,7 @@ impl PromPlanner {
input: LogicalPlan,
timestamp_value_column: &str,
) -> Result<LogicalPlan> {
let time_expr = col(timestamp_value_column).alias(DEFAULT_FIELD_COLUMN);
let time_expr = ident(timestamp_value_column).alias(DEFAULT_FIELD_COLUMN);
self.ctx.field_columns = vec![time_expr.schema_name().to_string()];
let mut project_exprs = Vec::with_capacity(self.ctx.tag_columns.len() + 2);
project_exprs.push(self.create_time_index_column_expr()?);
@@ -2507,7 +2509,7 @@ impl PromPlanner {
// collect remaining fields and convert to col expr
let mut exprs = all_fields
.into_iter()
.map(|c| DfExpr::Column(Column::from(c)))
.map(|c| DfExpr::Column(Column::from_name(c)))
.collect::<Vec<_>>();
// add timestamp column
@@ -4774,7 +4776,7 @@ impl PromPlanner {
.map(|col| {
let mut sort_exprs = Vec::with_capacity(self.ctx.tag_columns.len() + 1);
// Order by value in the specific order
sort_exprs.push(DfExpr::Column(Column::from(col)).sort(asc, true));
sort_exprs.push(DfExpr::Column(Column::from_name(col)).sort(asc, true));
// Then tags if the values are equal,
// Try to ensure the relative stability of the output results.
sort_exprs.extend(tag_sort_exprs.clone());
@@ -5547,7 +5549,7 @@ impl PromPlanner {
.collect::<Vec<_>>();
let assert_expr = DfExpr::ScalarFunction(ScalarFunction {
func: Arc::new(UniqueMatchGroup::scalar_udf(group_labels, violation)),
args: std::iter::once(col(count_column.as_str()))
args: std::iter::once(ident(count_column.as_str()))
.chain(group_exprs)
.collect(),
});
@@ -6186,7 +6188,7 @@ impl PromPlanner {
/// Generate an expr like `date_part("hour", <TIME_INDEX>)`. Caller should ensure the
/// time index column in context is set
fn date_part_on_time_index(&self, date_part: &str) -> Result<DfExpr> {
let input_expr = datafusion::logical_expr::col(
let input_expr = datafusion::logical_expr::ident(
self.ctx
.time_index_column
.as_ref()
@@ -28,7 +28,7 @@ use datafusion::scalar::ScalarValue;
use datafusion_common::DFSchema;
use datafusion_expr::expr_fn::when;
use datafusion_expr::simplify::SimplifyContext;
use datafusion_expr::{col, lit};
use datafusion_expr::{ident, lit};
use datafusion_functions::core::coalesce;
use datatypes::arrow::datatypes::DataType as ArrowDataType;
use promql::extension_plan::{
@@ -253,12 +253,12 @@ impl PromPlanner {
"vector contains a mix of classic and native histograms".to_string(),
self.promql_annotations.clone(),
)),
args: vec![col(&float_field), col(&histogram_field)],
args: vec![ident(&float_field), ident(&histogram_field)],
});
let keep = when(
col(&float_field)
ident(&float_field)
.is_not_null()
.and(col(&histogram_field).is_not_null()),
.and(ident(&histogram_field).is_not_null()),
record_collision,
)
.otherwise(lit(true))
@@ -268,7 +268,7 @@ impl PromPlanner {
let output_field = native_expr.schema_name().to_string();
let value = DfExpr::ScalarFunction(ScalarFunction {
func: coalesce(),
args: vec![col(&float_field), native_expr],
args: vec![ident(&float_field), native_expr],
});
self.ctx.field_columns = vec![output_field.clone()];
LogicalPlanBuilder::from(LogicalPlan::Extension(Extension {
@@ -277,9 +277,9 @@ impl PromPlanner {
.filter(keep)
.context(DataFusionPlanningSnafu)?
.project(
std::iter::once(col(&time_index_column))
std::iter::once(ident(&time_index_column))
.chain(std::iter::once(value.alias(output_field)))
.chain(tag_columns.iter().map(col)),
.chain(tag_columns.iter().map(ident)),
)
.context(DataFusionPlanningSnafu)?
.build()
@@ -764,6 +764,7 @@ impl PromPlanner {
let join_keys = left_tag_col_set
.into_iter()
.chain([left_time_index])
.map(Column::from_name)
.collect::<Vec<_>>();
ensure!(
+5 -3
View File
@@ -36,7 +36,7 @@ use datafusion::datasource::memory::MemorySourceConfig;
use datafusion::datasource::source::DataSourceExec;
use datafusion::datasource::{MemTable, provider_as_source};
use datafusion::execution::context::SessionContext;
use datafusion::logical_expr::Extension;
use datafusion::logical_expr::{Extension, col};
use datatypes::prelude::ConcreteDataType;
use datatypes::schema::{ColumnSchema, Schema};
use promql::extension_plan::HistogramFold;
@@ -1210,8 +1210,10 @@ fn classic_and_native_histogram_table_provider(
let table_name = "mixed_histogram";
let catalog = MemoryCatalogManager::with_default_setup();
let schema = Arc::new(Schema::new(vec![
// A dotted tag name guards the mixed histogram_quantile projection
// against qualified-name parsing (#9390).
ColumnSchema::new(
"tag".to_string(),
"service.name".to_string(),
ConcreteDataType::string_datatype(),
false,
),
@@ -4386,7 +4388,7 @@ async fn mixed_histogram_helpers_execute_classic_and_native_samples() {
.iter()
.flat_map(|batch| {
let tags = batch
.column_by_name("tag")
.column_by_name("service.name")
.unwrap()
.as_any()
.downcast_ref::<StringArray>()
+23 -8
View File
@@ -36,7 +36,7 @@ use common_query::prelude::{greptime_timestamp, greptime_value};
use common_recordbatch::{RecordBatch, RecordBatches};
use common_telemetry::{tracing, warn};
use datafusion::dataframe::DataFrame;
use datafusion::prelude::{Expr, col, lit, regexp_match};
use datafusion::prelude::{Expr, ident, lit, regexp_match};
use datafusion_common::ScalarValue;
use datafusion_expr::LogicalPlan;
use snafu::{OptionExt, ResultExt, ensure};
@@ -150,8 +150,9 @@ pub fn query_to_plan(
let mut conditions = Vec::with_capacity(label_matches.len() + 1);
conditions
.push(col(timestamp_column_name).gt_eq(lit_timestamp_millisecond(start_timestamp_ms)));
conditions.push(col(timestamp_column_name).lt_eq(lit_timestamp_millisecond(end_timestamp_ms)));
.push(ident(timestamp_column_name).gt_eq(lit_timestamp_millisecond(start_timestamp_ms)));
conditions
.push(ident(timestamp_column_name).lt_eq(lit_timestamp_millisecond(end_timestamp_ms)));
for m in label_matches {
let name = &m.name;
@@ -170,18 +171,18 @@ pub fn query_to_plan(
match m_type {
MatcherType::Eq => {
conditions.push(col(name).eq(lit(value)));
conditions.push(ident(name).eq(lit(value)));
}
MatcherType::Neq => {
conditions.push(col(name).not_eq(lit(value)));
conditions.push(ident(name).not_eq(lit(value)));
}
// Case sensitive regexp match
MatcherType::Re => {
conditions.push(regexp_match(col(name), lit(value), None).is_not_null());
conditions.push(regexp_match(ident(name), lit(value), None).is_not_null());
}
// Case sensitive regexp not match
MatcherType::Nre => {
conditions.push(regexp_match(col(name), lit(value), None).is_null());
conditions.push(regexp_match(ident(name), lit(value), None).is_null());
}
}
}
@@ -890,6 +891,8 @@ mod tests {
ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
ColumnSchema::new("job", ConcreteDataType::string_datatype(), true),
ColumnSchema::new("service.name", ConcreteDataType::string_datatype(), true),
ColumnSchema::new("Region", ConcreteDataType::string_datatype(), true),
]));
let recordbatch = RecordBatch::new(
schema,
@@ -898,6 +901,8 @@ mod tests {
Arc::new(Float64Vector::from_vec(vec![3.0])) as _,
Arc::new(StringVector::from(vec!["host1"])) as _,
Arc::new(StringVector::from(vec!["job"])) as _,
Arc::new(StringVector::from(vec!["api"])) as _,
Arc::new(StringVector::from(vec!["us"])) as _,
],
)
.unwrap();
@@ -936,6 +941,16 @@ mod tests {
value: "localhost".to_string(),
r#type: NEQ_TYPE,
},
LabelMatcher {
name: "service.name".to_string(),
value: "api".to_string(),
r#type: EQ_TYPE,
},
LabelMatcher {
name: "Region".to_string(),
value: "us".to_string(),
r#type: EQ_TYPE,
},
],
..Default::default()
};
@@ -946,7 +961,7 @@ mod tests {
let ts_col = greptime_timestamp();
let expected = format!(
"Filter: ?table?.{} >= TimestampMillisecond(1000, None) AND ?table?.{} <= TimestampMillisecond(2000, None) AND regexp_match(?table?.job, Utf8(\"*prom*\")) IS NOT NULL AND ?table?.instance != Utf8(\"localhost\")\n TableScan: ?table?",
"Filter: ?table?.{} >= TimestampMillisecond(1000, None) AND ?table?.{} <= TimestampMillisecond(2000, None) AND regexp_match(?table?.job, Utf8(\"*prom*\")) IS NOT NULL AND ?table?.instance != Utf8(\"localhost\") AND ?table?.service.name = Utf8(\"api\") AND ?table?.Region = Utf8(\"us\")\n TableScan: ?table?",
ts_col, ts_col
);
assert_eq!(expected, display_string);
@@ -0,0 +1,505 @@
-- https://github.com/GreptimeTeam/greptimedb/issues/9390
-- Tag, field and time index names containing dots or upper case letters must
-- be resolved as plain column names, not as `relation.column`. `Host` and `host`
-- hold different values, so reading one for the other changes the output.
CREATE TABLE "otel.m" (
"ts.time" TIMESTAMP(3) TIME INDEX,
"service.name" STRING,
"Host" STRING,
host STRING,
"v.val" DOUBLE,
PRIMARY KEY ("service.name", "Host", host)
) PARTITION ON COLUMNS ("service.name") (
"service.name" < 'b',
"service.name" >= 'b'
);
Affected Rows: 0
INSERT INTO "otel.m" VALUES
(0, 'a', 'h1', 'x', 1),
(0, 'a', 'h2', 'x', 2),
(0, 'b', 'h1', 'y', 3),
(5000, 'a', 'h1', 'x', 4),
(5000, 'a', 'h2', 'x', 5),
(5000, 'b', 'h1', 'y', 6),
(10000, 'a', 'h1', 'x', 8),
(10000, 'a', 'h2', 'x', 9),
(10000, 'b', 'h1', 'y', 8);
Affected Rows: 9
CREATE TABLE "otel.h" (
"ts.time" TIMESTAMP(3) TIME INDEX,
"service.name" STRING,
le STRING,
"v.val" DOUBLE,
PRIMARY KEY ("service.name", le)
);
Affected Rows: 0
INSERT INTO "otel.h" VALUES
(0, 'a', '0.1', 1),
(0, 'a', '1', 3),
(0, 'a', '+Inf', 4),
(10000, 'a', '0.1', 2),
(10000, 'a', '1', 6),
(10000, 'a', '+Inf', 10);
Affected Rows: 6
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} and {"otel.m", "Host"="h1"};
+---------------------+--------------+------+------+-------+
| ts.time | service.name | Host | host | v.val |
+---------------------+--------------+------+------+-------+
| 1970-01-01T00:00:00 | a | h1 | x | 1.0 |
| 1970-01-01T00:00:00 | b | h1 | y | 3.0 |
| 1970-01-01T00:00:05 | a | h1 | x | 4.0 |
| 1970-01-01T00:00:05 | b | h1 | y | 6.0 |
| 1970-01-01T00:00:10 | a | h1 | x | 8.0 |
| 1970-01-01T00:00:10 | b | h1 | y | 8.0 |
+---------------------+--------------+------+------+-------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} and on("service.name") {"otel.m", "service.name"="b"};
+---------------------+--------------+------+------+-------+
| ts.time | service.name | Host | host | v.val |
+---------------------+--------------+------+------+-------+
| 1970-01-01T00:00:00 | b | h1 | y | 3.0 |
| 1970-01-01T00:00:05 | b | h1 | y | 6.0 |
| 1970-01-01T00:00:10 | b | h1 | y | 8.0 |
+---------------------+--------------+------+------+-------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} and on(host) {"otel.m", host="y"};
+---------------------+--------------+------+------+-------+
| ts.time | service.name | Host | host | v.val |
+---------------------+--------------+------+------+-------+
| 1970-01-01T00:00:00 | b | h1 | y | 3.0 |
| 1970-01-01T00:00:05 | b | h1 | y | 6.0 |
| 1970-01-01T00:00:10 | b | h1 | y | 8.0 |
+---------------------+--------------+------+------+-------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} unless {"otel.m", "Host"="h1"};
+---------------------+--------------+------+------+-------+
| ts.time | service.name | Host | host | v.val |
+---------------------+--------------+------+------+-------+
| 1970-01-01T00:00:00 | a | h2 | x | 2.0 |
| 1970-01-01T00:00:05 | a | h2 | x | 5.0 |
| 1970-01-01T00:00:10 | a | h2 | x | 9.0 |
+---------------------+--------------+------+------+-------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} unless on("service.name") {"otel.m", "service.name"="b"};
+---------------------+--------------+------+------+-------+
| ts.time | service.name | Host | host | v.val |
+---------------------+--------------+------+------+-------+
| 1970-01-01T00:00:00 | a | h1 | x | 1.0 |
| 1970-01-01T00:00:00 | a | h2 | x | 2.0 |
| 1970-01-01T00:00:05 | a | h1 | x | 4.0 |
| 1970-01-01T00:00:05 | a | h2 | x | 5.0 |
| 1970-01-01T00:00:10 | a | h1 | x | 8.0 |
| 1970-01-01T00:00:10 | a | h2 | x | 9.0 |
+---------------------+--------------+------+------+-------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} or {"otel.m", "Host"="h1"};
+---------------------+------+------+--------------+-------+
| ts.time | Host | host | service.name | v.val |
+---------------------+------+------+--------------+-------+
| 1970-01-01T00:00:00 | h1 | x | a | 1.0 |
| 1970-01-01T00:00:00 | h1 | y | b | 3.0 |
| 1970-01-01T00:00:00 | h2 | x | a | 2.0 |
| 1970-01-01T00:00:05 | h1 | x | a | 4.0 |
| 1970-01-01T00:00:05 | h1 | y | b | 6.0 |
| 1970-01-01T00:00:05 | h2 | x | a | 5.0 |
| 1970-01-01T00:00:10 | h1 | x | a | 8.0 |
| 1970-01-01T00:00:10 | h1 | y | b | 8.0 |
| 1970-01-01T00:00:10 | h2 | x | a | 9.0 |
+---------------------+------+------+--------------+-------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') count_values("v.name", {"otel.m"});
+---------------------+---------------------+--------+
| count(otel.m.v.val) | ts.time | v.name |
+---------------------+---------------------+--------+
| 1 | 1970-01-01T00:00:00 | 1 |
| 1 | 1970-01-01T00:00:00 | 2 |
| 1 | 1970-01-01T00:00:00 | 3 |
| 1 | 1970-01-01T00:00:05 | 4 |
| 1 | 1970-01-01T00:00:05 | 5 |
| 1 | 1970-01-01T00:00:05 | 6 |
| 1 | 1970-01-01T00:00:10 | 9 |
| 2 | 1970-01-01T00:00:10 | 8 |
+---------------------+---------------------+--------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') count_values by ("service.name") ("v.name", {"otel.m"});
+---------------------+--------------+---------------------+--------+
| count(otel.m.v.val) | service.name | ts.time | v.name |
+---------------------+--------------+---------------------+--------+
| 1 | a | 1970-01-01T00:00:00 | 1 |
| 1 | a | 1970-01-01T00:00:00 | 2 |
| 1 | a | 1970-01-01T00:00:05 | 4 |
| 1 | a | 1970-01-01T00:00:05 | 5 |
| 1 | a | 1970-01-01T00:00:10 | 8 |
| 1 | a | 1970-01-01T00:00:10 | 9 |
| 1 | b | 1970-01-01T00:00:00 | 3 |
| 1 | b | 1970-01-01T00:00:05 | 6 |
| 1 | b | 1970-01-01T00:00:10 | 8 |
+---------------------+--------------+---------------------+--------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') topk(1, {"otel.m"});
+-------+--------------+------+------+---------------------+
| v.val | service.name | Host | host | ts.time |
+-------+--------------+------+------+---------------------+
| 3.0 | b | h1 | y | 1970-01-01T00:00:00 |
| 6.0 | b | h1 | y | 1970-01-01T00:00:05 |
| 9.0 | a | h2 | x | 1970-01-01T00:00:10 |
+-------+--------------+------+------+---------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') bottomk by ("service.name") (1, {"otel.m"});
+-------+--------------+------+------+---------------------+
| v.val | service.name | Host | host | ts.time |
+-------+--------------+------+------+---------------------+
| 1.0 | a | h1 | x | 1970-01-01T00:00:00 |
| 3.0 | b | h1 | y | 1970-01-01T00:00:00 |
| 4.0 | a | h1 | x | 1970-01-01T00:00:05 |
| 6.0 | b | h1 | y | 1970-01-01T00:00:05 |
| 8.0 | a | h1 | x | 1970-01-01T00:00:10 |
| 8.0 | b | h1 | y | 1970-01-01T00:00:10 |
+-------+--------------+------+------+---------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum by ("service.name") ({"otel.m"});
+--------------+---------------------+-------------------+
| service.name | ts.time | sum(otel.m.v.val) |
+--------------+---------------------+-------------------+
| a | 1970-01-01T00:00:00 | 3.0 |
| a | 1970-01-01T00:00:05 | 9.0 |
| a | 1970-01-01T00:00:10 | 17.0 |
| b | 1970-01-01T00:00:00 | 3.0 |
| b | 1970-01-01T00:00:05 | 6.0 |
| b | 1970-01-01T00:00:10 | 8.0 |
+--------------+---------------------+-------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum by ("Host") ({"otel.m"});
+------+---------------------+-------------------+
| Host | ts.time | sum(otel.m.v.val) |
+------+---------------------+-------------------+
| h1 | 1970-01-01T00:00:00 | 4.0 |
| h1 | 1970-01-01T00:00:05 | 10.0 |
| h1 | 1970-01-01T00:00:10 | 16.0 |
| h2 | 1970-01-01T00:00:00 | 2.0 |
| h2 | 1970-01-01T00:00:05 | 5.0 |
| h2 | 1970-01-01T00:00:10 | 9.0 |
+------+---------------------+-------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum by (host) ({"otel.m"});
+------+---------------------+-------------------+
| host | ts.time | sum(otel.m.v.val) |
+------+---------------------+-------------------+
| x | 1970-01-01T00:00:00 | 3.0 |
| x | 1970-01-01T00:00:05 | 9.0 |
| x | 1970-01-01T00:00:10 | 17.0 |
| y | 1970-01-01T00:00:00 | 3.0 |
| y | 1970-01-01T00:00:05 | 6.0 |
| y | 1970-01-01T00:00:10 | 8.0 |
+------+---------------------+-------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum without ("Host") ({"otel.m"});
+------+--------------+---------------------+-------------------+
| host | service.name | ts.time | sum(otel.m.v.val) |
+------+--------------+---------------------+-------------------+
| x | a | 1970-01-01T00:00:00 | 3.0 |
| x | a | 1970-01-01T00:00:05 | 9.0 |
| x | a | 1970-01-01T00:00:10 | 17.0 |
| y | b | 1970-01-01T00:00:00 | 3.0 |
| y | b | 1970-01-01T00:00:05 | 6.0 |
| y | b | 1970-01-01T00:00:10 | 8.0 |
+------+--------------+---------------------+-------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') -{"otel.m"};
+--------------+------+------+---------------------+-----------+
| service.name | Host | host | ts.time | (- v.val) |
+--------------+------+------+---------------------+-----------+
| a | h1 | x | 1970-01-01T00:00:00 | -1.0 |
| a | h1 | x | 1970-01-01T00:00:05 | -4.0 |
| a | h1 | x | 1970-01-01T00:00:10 | -8.0 |
| a | h2 | x | 1970-01-01T00:00:00 | -2.0 |
| a | h2 | x | 1970-01-01T00:00:05 | -5.0 |
| a | h2 | x | 1970-01-01T00:00:10 | -9.0 |
| b | h1 | y | 1970-01-01T00:00:00 | -3.0 |
| b | h1 | y | 1970-01-01T00:00:05 | -6.0 |
| b | h1 | y | 1970-01-01T00:00:10 | -8.0 |
+--------------+------+------+---------------------+-----------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') 2 * {"otel.m"};
+--------------+------+------+---------------------+--------------------+
| service.name | Host | host | ts.time | Float64(2) * v.val |
+--------------+------+------+---------------------+--------------------+
| a | h1 | x | 1970-01-01T00:00:00 | 2.0 |
| a | h1 | x | 1970-01-01T00:00:05 | 8.0 |
| a | h1 | x | 1970-01-01T00:00:10 | 16.0 |
| a | h2 | x | 1970-01-01T00:00:00 | 4.0 |
| a | h2 | x | 1970-01-01T00:00:05 | 10.0 |
| a | h2 | x | 1970-01-01T00:00:10 | 18.0 |
| b | h1 | y | 1970-01-01T00:00:00 | 6.0 |
| b | h1 | y | 1970-01-01T00:00:05 | 12.0 |
| b | h1 | y | 1970-01-01T00:00:10 | 16.0 |
+--------------+------+------+---------------------+--------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} - 1;
+--------------+------+------+---------------------+--------------------+
| service.name | Host | host | ts.time | v.val - Float64(1) |
+--------------+------+------+---------------------+--------------------+
| a | h1 | x | 1970-01-01T00:00:00 | 0.0 |
| a | h1 | x | 1970-01-01T00:00:05 | 3.0 |
| a | h1 | x | 1970-01-01T00:00:10 | 7.0 |
| a | h2 | x | 1970-01-01T00:00:00 | 1.0 |
| a | h2 | x | 1970-01-01T00:00:05 | 4.0 |
| a | h2 | x | 1970-01-01T00:00:10 | 8.0 |
| b | h1 | y | 1970-01-01T00:00:00 | 2.0 |
| b | h1 | y | 1970-01-01T00:00:05 | 5.0 |
| b | h1 | y | 1970-01-01T00:00:10 | 7.0 |
+--------------+------+------+---------------------+--------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') timestamp({"otel.m"});
+---------------------+-------+--------------+------+------+
| ts.time | value | service.name | Host | host |
+---------------------+-------+--------------+------+------+
| 1970-01-01T00:00:00 | 0.0 | a | h1 | x |
| 1970-01-01T00:00:00 | 0.0 | a | h2 | x |
| 1970-01-01T00:00:00 | 0.0 | b | h1 | y |
| 1970-01-01T00:00:05 | 5.0 | a | h1 | x |
| 1970-01-01T00:00:05 | 5.0 | a | h2 | x |
| 1970-01-01T00:00:05 | 5.0 | b | h1 | y |
| 1970-01-01T00:00:10 | 10.0 | a | h1 | x |
| 1970-01-01T00:00:10 | 10.0 | a | h2 | x |
| 1970-01-01T00:00:10 | 10.0 | b | h1 | y |
+---------------------+-------+--------------+------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') minute({"otel.m"});
+---------------------+-----------------------------------+--------------+------+------+
| ts.time | date_part(Utf8("minute"),ts.time) | service.name | Host | host |
+---------------------+-----------------------------------+--------------+------+------+
| 1970-01-01T00:00:00 | 0 | a | h1 | x |
| 1970-01-01T00:00:00 | 0 | a | h2 | x |
| 1970-01-01T00:00:00 | 0 | b | h1 | y |
| 1970-01-01T00:00:05 | 0 | a | h1 | x |
| 1970-01-01T00:00:05 | 0 | a | h2 | x |
| 1970-01-01T00:00:05 | 0 | b | h1 | y |
| 1970-01-01T00:00:10 | 0 | a | h1 | x |
| 1970-01-01T00:00:10 | 0 | a | h2 | x |
| 1970-01-01T00:00:10 | 0 | b | h1 | y |
+---------------------+-----------------------------------+--------------+------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (5, 10, '5s') sum by ("service.name") (increase({"otel.m"}[10s]));
+--------------+---------------------+--------------------------------------------------------------+
| service.name | ts.time | sum(prom_increase(ts.time_range,v.val,ts.time,Int64(10000))) |
+--------------+---------------------+--------------------------------------------------------------+
| a | 1970-01-01T00:00:05 | 9.0 |
| a | 1970-01-01T00:00:10 | 16.0 |
| b | 1970-01-01T00:00:05 | 6.0 |
| b | 1970-01-01T00:00:10 | 4.0 |
+--------------+---------------------+--------------------------------------------------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') scalar(sum({"otel.m"})) * {"otel.m", "Host"="h2"};
+--------------+------+------+---------------------+-------------------------------------------+
| service.name | Host | host | ts.time | lhs.scalar(sum(otel.m.v.val)) * rhs.v.val |
+--------------+------+------+---------------------+-------------------------------------------+
| a | h2 | x | 1970-01-01T00:00:00 | 12.0 |
| a | h2 | x | 1970-01-01T00:00:05 | 75.0 |
| a | h2 | x | 1970-01-01T00:00:10 | 225.0 |
+--------------+------+------+---------------------+-------------------------------------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') absent({"otel.m", "service.name"="c"});
+---------------------+-------+--------------+
| ts.time | v.val | service.name |
+---------------------+-------+--------------+
| 1970-01-01T00:00:00 | 1.0 | c |
| 1970-01-01T00:00:05 | 1.0 | c |
| 1970-01-01T00:00:10 | 1.0 | c |
+---------------------+-------+--------------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') histogram_quantile(0.5, {"otel.h"});
+---------------------+--------------+-------+
| ts.time | service.name | v.val |
+---------------------+--------------+-------+
| 1970-01-01T00:00:00 | a | 0.55 |
| 1970-01-01T00:00:05 | a | 0.55 |
| 1970-01-01T00:00:10 | a | 0.775 |
+---------------------+--------------+-------+
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (0, 10, '5s') {"otel.m"} and {"otel.m", "Host"="h1"};
+-+-+
| plan_type_| plan_|
+-+-+
| logical_plan_| LeftSemi Join: otel.m.Host = otel.m.Host, otel.m.host = otel.m.host, otel.m.service.name = otel.m.service.name, otel.m.ts.time = otel.m.ts.time_|
|_|_Aggregate: groupBy=[[otel.m.ts.time, otel.m.service.name, otel.m.Host, otel.m.host, otel.m.v.val]], aggr=[[]]_|
|_|_MergeScan [is_placeholder=false, remote_input=[_|
|_| Distinct:_|
|_|_PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts.time]_|
|_|_PromSeriesDivide: tags=["service.name", "Host", "host"]_|
|_|_Sort: otel.m.service.name ASC NULLS FIRST, otel.m.Host ASC NULLS FIRST, otel.m.host ASC NULLS FIRST, otel.m.ts.time ASC NULLS FIRST_|
|_|_Filter: otel.m.ts.time >= TimestampMillisecond(-299999, None) AND otel.m.ts.time <= TimestampMillisecond(10000, None)_|
|_|_TableScan: otel.m, partial_filters=[otel.m.ts.time >= TimestampMillisecond(-299999, None), otel.m.ts.time <= TimestampMillisecond(10000, None)]_|
|_| ]]_|
|_|_Projection: otel.m.ts.time, otel.m.service.name, otel.m.Host, otel.m.host_|
|_|_PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts.time]_|
|_|_PromSeriesDivide: tags=["service.name", "Host", "host"]_|
|_|_Sort: otel.m.service.name ASC NULLS FIRST, otel.m.Host ASC NULLS FIRST, otel.m.host ASC NULLS FIRST, otel.m.ts.time ASC NULLS FIRST_|
|_|_MergeScan [is_placeholder=false, remote_input=[_|
|_| Filter: otel.m.Host = Utf8("h1") AND otel.m.ts.time >= TimestampMillisecond(-299999, None) AND otel.m.ts.time <= TimestampMillisecond(10000, None)_|
|_|_TableScan: otel.m, partial_filters=[otel.m.Host = Utf8("h1"), otel.m.ts.time >= TimestampMillisecond(-299999, None), otel.m.ts.time <= TimestampMillisecond(10000, None)] |
|_| ]]_|
| physical_plan | HashJoinExec: mode=CollectLeft, join_type=RightSemi, on=[(Host@2, Host@2), (host@3, host@3), (service.name@1, service.name@1), (ts.time@0, ts.time@0)], NullsEqual: true_|
|_|_CoalescePartitionsExec_|
|_|_ProjectionExec: expr=[ts.time@0 as ts.time, service.name@1 as service.name, Host@2 as Host, host@3 as host]_|
|_|_PromInstantManipulateExec: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts.time]_|
|_|_PromSeriesDivideExec: tags=["service.name", "Host", "host"]_|
|_|_SortExec: expr=[service.name@1 ASC, Host@2 ASC, host@3 ASC, ts.time@0 ASC], preserve_REDACTED
|_|_RepartitionExec: REDACTED
|_|_MergeScanExec: REDACTED
|_|_AggregateExec: mode=FinalPartitioned, gby=[ts.time@0 as ts.time, service.name@1 as service.name, Host@2 as Host, host@3 as host, v.val@4 as v.val], aggr=[]_|
|_|_RepartitionExec: REDACTED
|_|_AggregateExec: mode=Partial, gby=[ts.time@0 as ts.time, service.name@1 as service.name, Host@2 as Host, host@3 as host, v.val@4 as v.val], aggr=[]_|
|_|_RepartitionExec: REDACTED
|_|_MergeScanExec: REDACTED
|_|_|
+-+-+
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (0, 10, '5s') topk(1, {"otel.m"});
+-+-+
| plan_type_| plan_|
+-+-+
| logical_plan_| Projection: otel.m.v.val, otel.m.service.name, otel.m.Host, otel.m.host, otel.m.ts.time_|
|_|_Sort: otel.m.ts.time ASC NULLS LAST, row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ASC NULLS LAST_|
|_|_Filter: CAST(row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW AS Float64) <= Float64(1)_|
|_|_WindowAggr: windowExpr=[[row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW]]_|
|_|_MergeScan [is_placeholder=false, remote_input=[_|
|_| PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts.time]_|
|_|_PromSeriesDivide: tags=["service.name", "Host", "host"]_|
|_|_Sort: otel.m.service.name ASC NULLS FIRST, otel.m.Host ASC NULLS FIRST, otel.m.host ASC NULLS FIRST, otel.m.ts.time ASC NULLS FIRST_|
|_|_Filter: otel.m.ts.time >= TimestampMillisecond(-299999, None) AND otel.m.ts.time <= TimestampMillisecond(10000, None)_|
|_|_TableScan: otel.m, partial_filters=[otel.m.ts.time >= TimestampMillisecond(-299999, None), otel.m.ts.time <= TimestampMillisecond(10000, None)]_|
|_| ]]_|
| physical_plan | ProjectionExec: expr=[v.val@4 as v.val, service.name@1 as service.name, Host@2 as Host, host@3 as host, ts.time@0 as ts.time]_|
|_|_SortPreservingMergeExec: [ts.time@0 ASC NULLS LAST, row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW@5 ASC NULLS LAST]_|
|_|_FilterExec: CAST(row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW@5 AS Float64) <= 1_|
|_|_BoundedWindowAggExec: wdw=[row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Field { "row_number() PARTITION BY [otel.m.ts.time] ORDER BY [otel.m.v.val DESC NULLS FIRST, otel.m.service.name DESC NULLS FIRST, otel.m.Host DESC NULLS FIRST, otel.m.host DESC NULLS FIRST] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW": UInt64 }, frame: ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW], mode=[Sorted] |
|_|_SortExec: expr=[ts.time@0 ASC NULLS LAST, v.val@4 DESC, service.name@1 DESC, Host@2 DESC, host@3 DESC], preserve_REDACTED
|_|_RepartitionExec: REDACTED
|_|_MergeScanExec: REDACTED
|_|_|
+-+-+
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (5, 10, '5s') sum by ("service.name") (increase({"otel.m"}[10s]));
+-+-+
| plan_type_| plan_|
+-+-+
| logical_plan_| MergeSort: otel.m.service.name ASC NULLS LAST, otel.m.ts.time ASC NULLS LAST_|
|_|_MergeScan [is_placeholder=false, remote_input=[_|
|_| Sort: otel.m.service.name ASC NULLS LAST, otel.m.ts.time ASC NULLS LAST_|
|_|_Aggregate: groupBy=[[otel.m.service.name, otel.m.ts.time]], aggr=[[sum(prom_increase(ts.time_range,v.val,ts.time,Int64(10000)))]]_|
|_|_Filter: prom_increase(ts.time_range,v.val,ts.time,Int64(10000)) IS NOT NULL_|
|_|_Projection: otel.m.ts.time, prom_increase(ts.time_range, v.val, otel.m.ts.time, Int64(10000)) AS prom_increase(ts.time_range,v.val,ts.time,Int64(10000)), otel.m.service.name, otel.m.Host, otel.m.host |
|_|_PromRangeManipulate: req range=[5000..10000], interval=[5000], eval range=[10000], time index=[ts.time], values=["v.val"]_|
|_|_PromSeriesNormalize: offset=[0], time index=[ts.time], filter NaN: [true]_|
|_|_PromSeriesDivide: tags=["service.name", "Host", "host"]_|
|_|_Sort: otel.m.service.name ASC NULLS FIRST, otel.m.Host ASC NULLS FIRST, otel.m.host ASC NULLS FIRST, otel.m.ts.time ASC NULLS FIRST_|
|_|_Filter: otel.m.ts.time >= TimestampMillisecond(-4999, None) AND otel.m.ts.time <= TimestampMillisecond(10000, None)_|
|_|_TableScan: otel.m, partial_filters=[otel.m.ts.time >= TimestampMillisecond(-4999, None), otel.m.ts.time <= TimestampMillisecond(10000, None)]_|
|_| ]]_|
| physical_plan | MergeSortExec: [service.name@0 ASC NULLS LAST, ts.time@1 ASC NULLS LAST]_|
|_|_MergeScanExec: REDACTED
|_|_|
+-+-+
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (0, 10, '5s') histogram_quantile(0.5, {"otel.h"});
+-+-+
| plan_type_| plan_|
+-+-+
| logical_plan_| HistogramFold: le=le, field=v.val, quantile=0.5_|
|_|_MergeScan [is_placeholder=false, remote_input=[_|
|_| PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts.time]_|
|_|_PromSeriesDivide: tags=["service.name", "le"]_|
|_|_Sort: otel.h.service.name ASC NULLS FIRST, otel.h.le ASC NULLS FIRST, otel.h.ts.time ASC NULLS FIRST_|
|_|_Filter: otel.h.ts.time >= TimestampMillisecond(-299999, None) AND otel.h.ts.time <= TimestampMillisecond(10000, None)_|
|_|_TableScan: otel.h, partial_filters=[otel.h.ts.time >= TimestampMillisecond(-299999, None), otel.h.ts.time <= TimestampMillisecond(10000, None)] |
|_| ]]_|
| physical_plan | HistogramFoldExec: le=@2, field=@3, quantile=0.5_|
|_|_RepartitionExec: REDACTED
|_|_SortExec: expr=[service.name@1 ASC NULLS LAST, ts.time@0 ASC NULLS LAST, TRY_CAST(le@2 AS Float64) ASC NULLS LAST], preserve_REDACTED
|_|_MergeScanExec: REDACTED
|_|_|
+-+-+
DROP TABLE "otel.m";
Affected Rows: 0
DROP TABLE "otel.h";
Affected Rows: 0
@@ -0,0 +1,139 @@
-- https://github.com/GreptimeTeam/greptimedb/issues/9390
-- Tag, field and time index names containing dots or upper case letters must
-- be resolved as plain column names, not as `relation.column`. `Host` and `host`
-- hold different values, so reading one for the other changes the output.
CREATE TABLE "otel.m" (
"ts.time" TIMESTAMP(3) TIME INDEX,
"service.name" STRING,
"Host" STRING,
host STRING,
"v.val" DOUBLE,
PRIMARY KEY ("service.name", "Host", host)
) PARTITION ON COLUMNS ("service.name") (
"service.name" < 'b',
"service.name" >= 'b'
);
INSERT INTO "otel.m" VALUES
(0, 'a', 'h1', 'x', 1),
(0, 'a', 'h2', 'x', 2),
(0, 'b', 'h1', 'y', 3),
(5000, 'a', 'h1', 'x', 4),
(5000, 'a', 'h2', 'x', 5),
(5000, 'b', 'h1', 'y', 6),
(10000, 'a', 'h1', 'x', 8),
(10000, 'a', 'h2', 'x', 9),
(10000, 'b', 'h1', 'y', 8);
CREATE TABLE "otel.h" (
"ts.time" TIMESTAMP(3) TIME INDEX,
"service.name" STRING,
le STRING,
"v.val" DOUBLE,
PRIMARY KEY ("service.name", le)
);
INSERT INTO "otel.h" VALUES
(0, 'a', '0.1', 1),
(0, 'a', '1', 3),
(0, 'a', '+Inf', 4),
(10000, 'a', '0.1', 2),
(10000, 'a', '1', 6),
(10000, 'a', '+Inf', 10);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} and {"otel.m", "Host"="h1"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} and on("service.name") {"otel.m", "service.name"="b"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} and on(host) {"otel.m", host="y"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} unless {"otel.m", "Host"="h1"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} unless on("service.name") {"otel.m", "service.name"="b"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} or {"otel.m", "Host"="h1"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') count_values("v.name", {"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') count_values by ("service.name") ("v.name", {"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') topk(1, {"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') bottomk by ("service.name") (1, {"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum by ("service.name") ({"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum by ("Host") ({"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum by (host) ({"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') sum without ("Host") ({"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') -{"otel.m"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') 2 * {"otel.m"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') {"otel.m"} - 1;
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') timestamp({"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') minute({"otel.m"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (5, 10, '5s') sum by ("service.name") (increase({"otel.m"}[10s]));
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') scalar(sum({"otel.m"})) * {"otel.m", "Host"="h2"};
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') absent({"otel.m", "service.name"="c"});
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 10, '5s') histogram_quantile(0.5, {"otel.h"});
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (0, 10, '5s') {"otel.m"} and {"otel.m", "Host"="h1"};
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (0, 10, '5s') topk(1, {"otel.m"});
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (5, 10, '5s') sum by ("service.name") (increase({"otel.m"}[10s]));
-- SQLNESS REPLACE (peers.*) REDACTED
-- SQLNESS REPLACE (partitioning.*) REDACTED
-- SQLNESS REPLACE (-+) -
-- SQLNESS REPLACE (\s\s+) _
TQL EXPLAIN (0, 10, '5s') histogram_quantile(0.5, {"otel.h"});
DROP TABLE "otel.m";
DROP TABLE "otel.h";