feat: support PromQL function vector (#3036)

* produce vector plan

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* work with OR

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* apply review sugg

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* move common const strings to common_query

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

* add comment for GREPTIME_COUNT

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>

---------

Signed-off-by: Ruihang Xia <waynestxia@gmail.com>
This commit is contained in:
Ruihang Xia
2024-01-09 03:44:00 +00:00
committed by GitHub
parent 26cbcb8b3a
commit af0c4c068a
15 changed files with 359 additions and 268 deletions
Generated
+1
View File
@@ -6558,6 +6558,7 @@ dependencies = [
"common-catalog",
"common-error",
"common-macro",
"common-query",
"common-recordbatch",
"common-telemetry",
"datafusion",
+7
View File
@@ -18,3 +18,10 @@ pub use crate::columnar_value::ColumnarValue;
pub use crate::function::*;
pub use crate::logical_plan::{create_udf, AggregateFunction, Expr, ScalarUdf};
pub use crate::signature::{Signature, TypeSignature, Volatility};
/// Default timestamp column name for Prometheus metrics.
pub const GREPTIME_TIMESTAMP: &str = "greptime_timestamp";
/// Default value column name for Prometheus metrics.
pub const GREPTIME_VALUE: &str = "greptime_value";
/// Default counter column name for OTLP metrics.
pub const GREPTIME_COUNT: &str = "greptime_count";
+1
View File
@@ -13,6 +13,7 @@ catalog.workspace = true
common-catalog.workspace = true
common-error.workspace = true
common-macro.workspace = true
common-query.workspace = true
common-recordbatch.workspace = true
common-telemetry.workspace = true
datafusion.workspace = true
@@ -104,8 +104,8 @@ impl EmptyMetric {
.map(|expr| {
physical_planner.create_physical_expr(
expr,
&self.result_schema,
&ArrowSchema::from(self.result_schema.as_ref()),
&self.time_index_schema,
&ArrowSchema::from(self.time_index_schema.as_ref()),
session_state,
)
})
+166 -66
View File
@@ -19,6 +19,7 @@ use std::time::UNIX_EPOCH;
use async_recursion::async_recursion;
use catalog::table_source::DfTableSourceProvider;
use common_query::prelude::GREPTIME_VALUE;
use datafusion::common::{DFSchemaRef, OwnedTableReference, Result as DfResult};
use datafusion::datasource::DefaultTableSource;
use datafusion::logical_expr::expr::{AggregateFunction, Alias, ScalarFunction, ScalarUDF};
@@ -36,8 +37,9 @@ use datatypes::arrow::datatypes::DataType as ArrowDataType;
use promql_parser::label::{MatchOp, Matcher, Matchers, METRIC_NAME};
use promql_parser::parser::{
token, AggregateExpr, BinModifier, BinaryExpr as PromBinaryExpr, Call, EvalStmt,
Expr as PromExpr, Function, LabelModifier, MatrixSelector, NumberLiteral, Offset, ParenExpr,
StringLiteral, SubqueryExpr, TokenType, UnaryExpr, VectorMatchCardinality, VectorSelector,
Expr as PromExpr, Function, FunctionArgs as PromFunctionArgs, LabelModifier, MatrixSelector,
NumberLiteral, Offset, ParenExpr, StringLiteral, SubqueryExpr, TokenType, UnaryExpr,
VectorMatchCardinality, VectorSelector,
};
use snafu::{ensure, OptionExt, ResultExt};
use table::table::adapter::DfTableProviderAdapter;
@@ -63,6 +65,8 @@ use crate::functions::{
const SPECIAL_TIME_FUNCTION: &str = "time";
/// `histogram_quantile` function in PromQL
const SPECIAL_HISTOGRAM_QUANTILE: &str = "histogram_quantile";
/// `vector` function in PromQL
const SPECIAL_VECTOR_FUNCTION: &str = "vector";
/// `le` column for conventional histogram.
const LE_COLUMN_NAME: &str = "le";
@@ -289,13 +293,13 @@ 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 left_context = self.ctx.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();
let right_context = self.ctx.clone();
// TODO(ruihang): avoid join if left and right are the same table
@@ -304,8 +308,8 @@ impl PromPlanner {
return self.set_op_on_non_field_columns(
left_input,
right_input,
left_tag_cols,
right_tag_cols,
left_context,
right_context,
*op,
modifier,
);
@@ -463,57 +467,14 @@ impl PromPlanner {
})
}
PromExpr::Call(Call { func, args }) => {
if func.name == SPECIAL_HISTOGRAM_QUANTILE {
if args.args.len() != 2 {
return FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_HISTOGRAM_QUANTILE.to_string(),
}
.fail();
}
let phi = Self::try_build_float_literal(&args.args[0]).with_context(|| {
FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_HISTOGRAM_QUANTILE.to_string(),
}
})?;
let input = args.args[1].as_ref().clone();
let input_plan = self.prom_expr_to_plan(input).await?;
if !self.ctx.has_le_tag() {
return ColumnNotFoundSnafu {
col: LE_COLUMN_NAME.to_string(),
}
.fail();
}
let time_index_column =
self.ctx.time_index_column.clone().with_context(|| {
TimeIndexNotFoundSnafu {
table: self.ctx.table_name.clone().unwrap_or_default(),
}
})?;
// FIXME(ruihang): support multi fields
let field_column = self
.ctx
.field_columns
.first()
.with_context(|| FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_HISTOGRAM_QUANTILE.to_string(),
})?
.clone();
return Ok(LogicalPlan::Extension(Extension {
node: Arc::new(
HistogramFold::new(
LE_COLUMN_NAME.to_string(),
field_column,
time_index_column,
phi,
input_plan,
)
.context(DataFusionPlanningSnafu)?,
),
}));
// some special functions that are not expression but a plan
match func.name {
SPECIAL_HISTOGRAM_QUANTILE => return self.create_histogram_plan(args).await,
SPECIAL_VECTOR_FUNCTION => return self.create_vector_plan(args).await,
_ => {}
}
// transform function arguments
let args = self.create_function_args(&args.args)?;
let input = if let Some(prom_expr) = args.input {
self.prom_expr_to_plan(prom_expr).await?
@@ -1303,6 +1264,93 @@ impl PromPlanner {
Ok(exprs)
}
/// Create a [SPECIAL_HISTOGRAM_QUANTILE] plan.
async fn create_histogram_plan(&mut self, args: &PromFunctionArgs) -> Result<LogicalPlan> {
if args.args.len() != 2 {
return FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_HISTOGRAM_QUANTILE.to_string(),
}
.fail();
}
let phi = Self::try_build_float_literal(&args.args[0]).with_context(|| {
FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_HISTOGRAM_QUANTILE.to_string(),
}
})?;
let input = args.args[1].as_ref().clone();
let input_plan = self.prom_expr_to_plan(input).await?;
if !self.ctx.has_le_tag() {
return ColumnNotFoundSnafu {
col: LE_COLUMN_NAME.to_string(),
}
.fail();
}
let time_index_column =
self.ctx
.time_index_column
.clone()
.with_context(|| TimeIndexNotFoundSnafu {
table: self.ctx.table_name.clone().unwrap_or_default(),
})?;
// FIXME(ruihang): support multi fields
let field_column = self
.ctx
.field_columns
.first()
.with_context(|| FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_HISTOGRAM_QUANTILE.to_string(),
})?
.clone();
Ok(LogicalPlan::Extension(Extension {
node: Arc::new(
HistogramFold::new(
LE_COLUMN_NAME.to_string(),
field_column,
time_index_column,
phi,
input_plan,
)
.context(DataFusionPlanningSnafu)?,
),
}))
}
/// Create a [SPECIAL_VECTOR_FUNCTION] plan
async fn create_vector_plan(&mut self, args: &PromFunctionArgs) -> Result<LogicalPlan> {
if args.args.len() != 1 {
return FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_VECTOR_FUNCTION.to_string(),
}
.fail();
}
let lit = Self::try_build_float_literal(&args.args[0]).with_context(|| {
FunctionInvalidArgumentSnafu {
fn_name: SPECIAL_VECTOR_FUNCTION.to_string(),
}
})?;
// reuse `SPECIAL_TIME_FUNCTION` as name of time index column
self.ctx.time_index_column = Some(SPECIAL_TIME_FUNCTION.to_string());
self.ctx.table_name = Some(String::new());
self.ctx.tag_columns = vec![];
self.ctx.field_columns = vec![GREPTIME_VALUE.to_string()];
Ok(LogicalPlan::Extension(Extension {
node: Arc::new(
EmptyMetric::new(
self.ctx.start,
self.ctx.end,
self.ctx.interval,
SPECIAL_TIME_FUNCTION.to_string(),
GREPTIME_VALUE.to_string(),
Some(DfExpr::Literal(ScalarValue::Float64(Some(lit)))),
)
.context(DataFusionPlanningSnafu)?,
),
}))
}
/// Try to build a DataFusion Literal Expression from PromQL Expr, return
/// `None` if the input is not a literal expression.
fn try_build_literal_expr(expr: &PromExpr) -> Option<DfExpr> {
@@ -1491,19 +1539,35 @@ impl PromPlanner {
/// Build a set operator (AND/OR/UNLESS)
fn set_op_on_non_field_columns(
&self,
&mut self,
left: LogicalPlan,
right: LogicalPlan,
left_tag_cols: Vec<String>,
right_tag_cols: Vec<String>,
left_context: PromPlannerContext,
right_context: PromPlannerContext,
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<_>>();
let mut left_tag_col_set = left_context
.tag_columns
.iter()
.cloned()
.collect::<HashSet<_>>();
let mut right_tag_col_set = right_context
.tag_columns
.iter()
.cloned()
.collect::<HashSet<_>>();
if matches!(op.id(), token::T_LOR) {
return self.or_operator(left, right, left_tag_col_set, right_tag_col_set, modifier);
return self.or_operator(
left,
right,
left_tag_col_set,
right_tag_col_set,
left_context,
right_context,
modifier,
);
}
// apply modifier
@@ -1585,19 +1649,24 @@ impl PromPlanner {
.build()
.context(DataFusionPlanningSnafu),
token::T_LOR => {
self.or_operator(left, right, left_tag_col_set, right_tag_col_set, modifier)
// OR is handled at the beginning of this function, as it cannot
// be expressed using JOIN like AND and UNLESS.
unreachable!()
}
_ => UnexpectedTokenSnafu { token: op }.fail(),
}
}
// TODO(ruihang): change function name
#[allow(clippy::too_many_arguments)]
fn or_operator(
&self,
&mut self,
left: LogicalPlan,
right: LogicalPlan,
left_tag_cols_set: HashSet<String>,
right_tag_cols_set: HashSet<String>,
left_context: PromPlannerContext,
right_context: PromPlannerContext,
modifier: &Option<BinModifier>,
) -> Result<LogicalPlan> {
// prepare hash sets
@@ -1623,18 +1692,37 @@ impl PromPlanner {
.as_ref()
.map(|r| r.to_string())
.unwrap_or_default();
let left_time_index_column =
left_context
.time_index_column
.clone()
.with_context(|| TimeIndexNotFoundSnafu {
table: left_qualifier_string.clone(),
})?;
let right_time_index_column =
right_context
.time_index_column
.clone()
.with_context(|| TimeIndexNotFoundSnafu {
table: right_qualifier_string.clone(),
})?;
// step 0: fill all columns in output schema
let all_columns_set = left
let mut all_columns_set = left
.schema()
.fields()
.iter()
.chain(right.schema().fields().iter())
.map(|field| field.name().clone())
.collect::<HashSet<_>>();
// remove time index column
all_columns_set.remove(&left_time_index_column);
all_columns_set.remove(&right_time_index_column);
let mut all_columns = all_columns_set.into_iter().collect::<Vec<_>>();
// sort to ensure the generated schema is not volatile
all_columns.sort_unstable();
// use left time index column name as the result time index column name
all_columns.insert(0, left_time_index_column.clone());
// step 1: align schema using project, fill non-exist columns with null
let left_proj_exprs = all_columns.iter().map(|col| {
@@ -1644,13 +1732,21 @@ impl PromPlanner {
DfExpr::Column(Column::new(left_qualifier.clone(), col))
}
});
let right_proj_exprs = all_columns.iter().map(|col| {
let right_time_index_expr = DfExpr::Column(Column::new(
right_qualifier.clone(),
right_time_index_column,
))
.alias(left_time_index_column.clone());
let right_proj_exprs_without_time_index = all_columns.iter().skip(1).map(|col| {
if tags_not_in_right.contains(col) {
DfExpr::Literal(ScalarValue::Utf8(None)).alias(col.to_string())
} else {
DfExpr::Column(Column::new(right_qualifier.clone(), col))
}
});
let right_proj_exprs = [right_time_index_expr]
.into_iter()
.chain(right_proj_exprs_without_time_index);
let left_projected = LogicalPlanBuilder::from(left)
.project(left_proj_exprs)
@@ -1691,13 +1787,17 @@ impl PromPlanner {
left_projected,
right_projected,
match_columns,
self.ctx.time_index_column.clone().unwrap(),
left_time_index_column.clone(),
schema,
);
let result = LogicalPlan::Extension(Extension {
node: Arc::new(union_distinct_on),
});
// step 4: update context
self.ctx.time_index_column = Some(left_time_index_column);
self.ctx.tag_columns = all_tags.into_iter().collect();
Ok(result)
}
+1 -2
View File
@@ -25,6 +25,7 @@ use common_datasource::file_format::{infer_schemas, FileFormat, Format};
use common_datasource::lister::{Lister, Source};
use common_datasource::object_store::build_backend;
use common_datasource::util::find_dir_and_filename;
use common_query::prelude::GREPTIME_TIMESTAMP;
use common_query::Output;
use common_recordbatch::{RecordBatch, RecordBatches};
use common_time::Timestamp;
@@ -57,8 +58,6 @@ const NULLABLE_YES: &str = "YES";
const NULLABLE_NO: &str = "NO";
const PRI_KEY: &str = "PRI";
const GREPTIME_TIMESTAMP: &str = "greptime_timestamp";
static DESCRIBE_TABLE_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = Lazy::new(|| {
Arc::new(Schema::new(vec![
ColumnSchema::new(
+4 -3
View File
@@ -22,6 +22,7 @@ use common_catalog::consts::DEFAULT_SCHEMA_NAME;
use common_catalog::parse_catalog_and_schema_from_db_string;
use common_error::ext::ErrorExt;
use common_error::status_code::StatusCode;
use common_query::prelude::{GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use common_query::Output;
use common_recordbatch::RecordBatches;
use common_time::util::{current_time_rfc3339, yesterday_rfc3339};
@@ -43,7 +44,7 @@ use snafu::{Location, OptionExt, ResultExt};
use crate::error::{
CollectRecordbatchSnafu, Error, InternalSnafu, InvalidQuerySnafu, Result, UnexpectedResultSnafu,
};
use crate::prom_store::{FIELD_COLUMN_NAME, METRIC_NAME_LABEL, TIMESTAMP_COLUMN_NAME};
use crate::prom_store::METRIC_NAME_LABEL;
use crate::prometheus_handler::PrometheusHandlerRef;
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
@@ -499,8 +500,8 @@ pub async fn labels_query(
}
}
let _ = labels.remove(TIMESTAMP_COLUMN_NAME);
let _ = labels.remove(FIELD_COLUMN_NAME);
let _ = labels.remove(GREPTIME_TIMESTAMP);
let _ = labels.remove(GREPTIME_VALUE);
let mut sorted_labels: Vec<String> = labels.into_iter().collect();
sorted_labels.sort();
+3 -3
View File
@@ -22,6 +22,7 @@ use std::sync::Arc;
use api::v1::RowInsertRequests;
use async_trait::async_trait;
use common_query::prelude::{GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use common_runtime::Runtime;
use common_telemetry::logging::error;
use futures::StreamExt;
@@ -31,7 +32,6 @@ use self::codec::DataPoint;
use crate::error::Result;
use crate::opentsdb::connection::Connection;
use crate::opentsdb::handler::Handler;
use crate::prom_store::{FIELD_COLUMN_NAME, TIMESTAMP_COLUMN_NAME};
use crate::query_handler::OpentsdbProtocolHandlerRef;
use crate::row_writer::{self, MultiTableData};
use crate::server::{AbortableStream, BaseTcpServer, Server};
@@ -151,11 +151,11 @@ pub fn data_point_to_grpc_row_insert_requests(
row_writer::write_tags(table_data, tags.into_iter(), &mut one_row)?;
// value
row_writer::write_f64(table_data, FIELD_COLUMN_NAME, value, &mut one_row)?;
row_writer::write_f64(table_data, GREPTIME_VALUE, value, &mut one_row)?;
// timestamp
row_writer::write_ts_millis(
table_data,
TIMESTAMP_COLUMN_NAME,
GREPTIME_TIMESTAMP,
Some(timestamp),
&mut one_row,
)?;
+5 -7
View File
@@ -13,12 +13,10 @@
// limitations under the License.
use api::v1::{column, Column, ColumnDataType, InsertRequest as GrpcInsertRequest, SemanticType};
use common_query::prelude::{GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use crate::error::{self, Result};
pub const OPENTSDB_TIMESTAMP_COLUMN_NAME: &str = "greptime_timestamp";
pub const OPENTSDB_FIELD_COLUMN_NAME: &str = "greptime_value";
#[derive(Debug, Clone)]
pub struct DataPoint {
metric: String,
@@ -131,7 +129,7 @@ impl DataPoint {
let mut columns = Vec::with_capacity(2 + self.tags.len());
let ts_column = Column {
column_name: OPENTSDB_TIMESTAMP_COLUMN_NAME.to_string(),
column_name: GREPTIME_TIMESTAMP.to_string(),
values: Some(column::Values {
timestamp_millisecond_values: vec![self.ts_millis],
..Default::default()
@@ -143,7 +141,7 @@ impl DataPoint {
columns.push(ts_column);
let field_column = Column {
column_name: OPENTSDB_FIELD_COLUMN_NAME.to_string(),
column_name: GREPTIME_VALUE.to_string(),
values: Some(column::Values {
f64_values: vec![self.value],
..Default::default()
@@ -269,7 +267,7 @@ mod test {
assert_eq!(row_count, 1);
assert_eq!(columns.len(), 4);
assert_eq!(columns[0].column_name, OPENTSDB_TIMESTAMP_COLUMN_NAME);
assert_eq!(columns[0].column_name, GREPTIME_TIMESTAMP);
assert_eq!(
columns[0]
.values
@@ -279,7 +277,7 @@ mod test {
vec![1000]
);
assert_eq!(columns[1].column_name, OPENTSDB_FIELD_COLUMN_NAME);
assert_eq!(columns[1].column_name, GREPTIME_VALUE);
assert_eq!(columns[1].values.as_ref().unwrap().f64_values, vec![1.0]);
assert_eq!(columns[2].column_name, "tagk1");
-4
View File
@@ -15,7 +15,3 @@
pub mod metrics;
pub mod plugin;
pub mod trace;
const GREPTIME_TIMESTAMP: &str = "greptime_timestamp";
const GREPTIME_VALUE: &str = "greptime_value";
const GREPTIME_COUNT: &str = "greptime_count";
+1 -1
View File
@@ -14,11 +14,11 @@
use api::v1::{RowInsertRequests, Value};
use common_grpc::writer::Precision;
use common_query::prelude::{GREPTIME_COUNT, GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use opentelemetry_proto::tonic::collector::metrics::v1::ExportMetricsServiceRequest;
use opentelemetry_proto::tonic::common::v1::{any_value, KeyValue};
use opentelemetry_proto::tonic::metrics::v1::{metric, number_data_point, *};
use super::{GREPTIME_COUNT, GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use crate::error::Result;
use crate::row_writer::{self, MultiTableData, TableData};
+1 -1
View File
@@ -15,10 +15,10 @@
use api::v1::value::ValueData;
use api::v1::{ColumnDataType, RowInsertRequests};
use common_grpc::writer::Precision;
use common_query::prelude::{GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
use self::span::{parse_span, TraceSpan, TraceSpans};
use super::{GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use crate::error::Result;
use crate::row_writer::{self, MultiTableData, TableData};
+12 -24
View File
@@ -21,6 +21,7 @@ use std::hash::{Hash, Hasher};
use api::prom_store::remote::label_matcher::Type as MatcherType;
use api::prom_store::remote::{Label, Query, Sample, TimeSeries, WriteRequest};
use api::v1::RowInsertRequests;
use common_query::prelude::{GREPTIME_TIMESTAMP, GREPTIME_VALUE};
use common_recordbatch::{RecordBatch, RecordBatches};
use common_time::timestamp::TimeUnit;
use datafusion::prelude::{col, lit, regexp_match, Expr};
@@ -35,8 +36,6 @@ use snap::raw::{Decoder, Encoder};
use crate::error::{self, Result};
use crate::row_writer::{self, MultiTableData};
pub const TIMESTAMP_COLUMN_NAME: &str = "greptime_timestamp";
pub const FIELD_COLUMN_NAME: &str = "greptime_value";
pub const METRIC_NAME_LABEL: &str = "__name__";
/// Metrics for push gateway protocol
@@ -73,9 +72,8 @@ pub fn query_to_plan(dataframe: DataFrame, q: &Query) -> Result<LogicalPlan> {
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)));
conditions.push(col(GREPTIME_TIMESTAMP).gt_eq(lit_timestamp_millisecond(start_timestamp_ms)));
conditions.push(col(GREPTIME_TIMESTAMP).lt_eq(lit_timestamp_millisecond(end_timestamp_ms)));
for m in label_matches {
let name = &m.name;
@@ -204,9 +202,7 @@ fn collect_timeseries_ids(table_name: &str, recordbatch: &RecordBatch) -> Vec<Ti
));
for (i, column_schema) in recordbatch.schema.column_schemas().iter().enumerate() {
if column_schema.name == FIELD_COLUMN_NAME
|| column_schema.name == TIMESTAMP_COLUMN_NAME
{
if column_schema.name == GREPTIME_VALUE || column_schema.name == GREPTIME_TIMESTAMP {
continue;
}
@@ -239,7 +235,7 @@ pub fn recordbatches_to_timeseries(
}
fn recordbatch_to_timeseries(table: &str, recordbatch: RecordBatch) -> Result<Vec<TimeSeries>> {
let ts_column = recordbatch.column_by_name(TIMESTAMP_COLUMN_NAME).context(
let ts_column = recordbatch.column_by_name(GREPTIME_TIMESTAMP).context(
error::InvalidPromRemoteReadQueryResultSnafu {
msg: "missing greptime_timestamp column in query result",
},
@@ -254,7 +250,7 @@ fn recordbatch_to_timeseries(table: &str, recordbatch: RecordBatch) -> Result<Ve
}
);
let field_column = recordbatch.column_by_name(FIELD_COLUMN_NAME).context(
let field_column = recordbatch.column_by_name(GREPTIME_VALUE).context(
error::InvalidPromRemoteReadQueryResultSnafu {
msg: "missing greptime_value column in query result",
},
@@ -341,11 +337,11 @@ pub fn to_grpc_row_insert_requests(request: WriteRequest) -> Result<(RowInsertRe
});
row_writer::write_tags(table_data, kvs, &mut one_row)?;
// value
row_writer::write_f64(table_data, FIELD_COLUMN_NAME, *value, &mut one_row)?;
row_writer::write_f64(table_data, GREPTIME_VALUE, *value, &mut one_row)?;
// timestamp
row_writer::write_ts_millis(
table_data,
TIMESTAMP_COLUMN_NAME,
GREPTIME_TIMESTAMP,
Some(*timestamp),
&mut one_row,
)?;
@@ -494,15 +490,11 @@ mod tests {
let schema = Arc::new(Schema::new(vec![
ColumnSchema::new(
TIMESTAMP_COLUMN_NAME,
GREPTIME_TIMESTAMP,
ConcreteDataType::timestamp_millisecond_datatype(),
true,
),
ColumnSchema::new(
FIELD_COLUMN_NAME,
ConcreteDataType::float64_datatype(),
true,
),
ColumnSchema::new(GREPTIME_VALUE, ConcreteDataType::float64_datatype(), true),
ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
ColumnSchema::new("job", ConcreteDataType::string_datatype(), true),
]));
@@ -701,15 +693,11 @@ mod tests {
fn test_recordbatches_to_timeseries() {
let schema = Arc::new(Schema::new(vec![
ColumnSchema::new(
TIMESTAMP_COLUMN_NAME,
GREPTIME_TIMESTAMP,
ConcreteDataType::timestamp_millisecond_datatype(),
true,
),
ColumnSchema::new(
FIELD_COLUMN_NAME,
ConcreteDataType::float64_datatype(),
true,
),
ColumnSchema::new(GREPTIME_VALUE, ConcreteDataType::float64_datatype(), true),
ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
]));
@@ -6,7 +6,7 @@ create table http_requests (
job string,
instance string,
g string, -- for `group`
val double,
greptime_value double,
primary key (job, instance, g)
);
@@ -32,7 +32,7 @@ Affected Rows: 0
create table vector_matching_a(
ts timestamp time index,
l string primary key,
val double,
greptime_value double,
);
Affected Rows: 0
@@ -49,12 +49,12 @@ Affected Rows: 2
-- 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 |
+---------------------+-----+----------+--------+-------+
+---------------------+-----+----------+--------+----------------+
| ts | job | instance | g | greptime_value |
+---------------------+-----+----------+--------+----------------+
| 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
@@ -62,12 +62,12 @@ tql eval (3000, 3000, '1s') http_requests{g="canary"} and http_requests{instance
-- 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 |
+-----+----------+--------+---------------------+------------------+
+-----+----------+--------+---------------------+-----------------------------+
| job | instance | g | ts | greptime_value + 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
@@ -75,12 +75,12 @@ tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and http_requests{in
-- 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 |
+-----+----------+--------+---------------------+------------------+
+-----+----------+--------+---------------------+-----------------------------+
| job | instance | g | ts | greptime_value + 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
@@ -88,12 +88,12 @@ tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and on(instance, job
-- 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 |
+-----+----------+--------+---------------------+------------------+
+-----+----------+--------+---------------------+-----------------------------+
| job | instance | g | ts | greptime_value + 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
@@ -101,12 +101,12 @@ tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and on(instance) htt
-- 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 |
+-----+----------+--------+---------------------+------------------+
+-----+----------+--------+---------------------+-----------------------------+
| job | instance | g | ts | greptime_value + 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
@@ -114,12 +114,12 @@ tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and ignoring(g) http
-- 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 |
+-----+----------+--------+---------------------+------------------+
+-----+----------+--------+---------------------+-----------------------------+
| job | instance | g | ts | greptime_value + 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
@@ -133,18 +133,18 @@ tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) and ignoring(g, job)
-- SQLNESS SORT_RESULT 3 1
tql eval (3000, 3000, '1s') http_requests{g="canary"} or http_requests{g="production"};
+------------+----------+-----+---------------------+-------+
| g | instance | job | ts | val |
+------------+----------+-----+---------------------+-------+
| canary | 0 | api | 1970-01-01T00:50:00 | 300.0 |
| canary | 0 | app | 1970-01-01T00:50:00 | 700.0 |
| canary | 1 | api | 1970-01-01T00:50:00 | 400.0 |
| canary | 1 | app | 1970-01-01T00:50:00 | 800.0 |
| production | 0 | api | 1970-01-01T00:50:00 | 100.0 |
| production | 0 | app | 1970-01-01T00:50:00 | 500.0 |
| production | 1 | api | 1970-01-01T00:50:00 | 200.0 |
| production | 1 | app | 1970-01-01T00:50:00 | 600.0 |
+------------+----------+-----+---------------------+-------+
+---------------------+------------+----------------+----------+-----+
| ts | g | greptime_value | instance | job |
+---------------------+------------+----------------+----------+-----+
| 1970-01-01T00:50:00 | canary | 300.0 | 0 | api |
| 1970-01-01T00:50:00 | canary | 400.0 | 1 | api |
| 1970-01-01T00:50:00 | canary | 700.0 | 0 | app |
| 1970-01-01T00:50:00 | canary | 800.0 | 1 | app |
| 1970-01-01T00:50:00 | production | 100.0 | 0 | api |
| 1970-01-01T00:50:00 | production | 200.0 | 1 | api |
| 1970-01-01T00:50:00 | production | 500.0 | 0 | app |
| 1970-01-01T00:50:00 | production | 600.0 | 1 | app |
+---------------------+------------+----------------+----------+-----+
-- # On overlap the rhs samples must be dropped.
-- eval instant at 50m (http_requests{group="canary"} + 1) or http_requests{instance="1"}
@@ -157,7 +157,7 @@ tql eval (3000, 3000, '1s') http_requests{g="canary"} or http_requests{g="produc
-- SQLNESS SORT_RESULT 3 1
tql eval (3000, 3000, '1s') (http_requests{g="canary"} + 1) or http_requests{instance="1"};
Error: 1004(InvalidArguments), Internal error during building DataFusion plan: No field named http_requests.val. Valid fields are http_requests.job, http_requests.instance, http_requests.g, http_requests.ts, "val + Float64(1)".
Error: 1004(InvalidArguments), Internal error during building DataFusion plan: No field named http_requests.greptime_value. Valid fields are http_requests.job, http_requests.instance, http_requests.g, http_requests.ts, "greptime_value + Float64(1)".
-- # Matching only on instance excludes everything that has instance=0/1 but includes
-- # entries without the instance label.
@@ -172,7 +172,7 @@ Error: 1004(InvalidArguments), Internal error during building DataFusion plan: N
-- 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), Internal error during building DataFusion plan: No field named cpu_count.val. Valid fields are cpu_count.ts.
Error: 1004(InvalidArguments), Internal error during building DataFusion plan: No field named cpu_count.greptime_value. Valid fields are cpu_count.ts.
-- 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
@@ -185,7 +185,7 @@ Error: 1004(InvalidArguments), Internal error during building DataFusion plan: N
-- 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), Internal error during building DataFusion plan: No field named cpu_count.val. Valid fields are cpu_count.ts.
Error: 1004(InvalidArguments), Internal error during building DataFusion plan: No field named cpu_count.greptime_value. Valid fields are cpu_count.ts.
-- eval instant at 50m http_requests{group="canary"} unless http_requests{instance="0"}
-- http_requests{group="canary", instance="1", job="api-server"} 400
@@ -193,12 +193,12 @@ Error: 1004(InvalidArguments), Internal error during building DataFusion plan: N
-- 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 |
+---------------------+-----+----------+--------+-------+
+---------------------+-----+----------+--------+----------------+
| ts | job | instance | g | greptime_value |
+---------------------+-----+----------+--------+----------------+
| 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"};
@@ -212,12 +212,12 @@ tql eval (3000, 3000, '1s') http_requests{g="canary"} unless on(job) http_reques
-- 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 |
+---------------------+-----+----------+--------+-------+
+---------------------+-----+----------+--------+----------------+
| ts | job | instance | g | greptime_value |
+---------------------+-----+----------+--------+----------------+
| 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"};
@@ -231,12 +231,12 @@ tql eval (3000, 3000, '1s') http_requests{g="canary"} unless ignoring(g, instanc
-- 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 |
+---------------------+-----+----------+--------+-------+
+---------------------+-----+----------+--------+----------------+
| ts | job | instance | g | greptime_value |
+---------------------+-----+----------+--------+----------------+
| 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)
@@ -251,7 +251,7 @@ tql eval (3000, 3000, '1s') http_requests{g="canary"} unless ignoring(g) http_re
-- NOT SUPPORTED: `vector()`
tql eval (3000, 3000, '1s') http_requests AND ON (dummy) vector(1);
Error: 1004(InvalidArguments), Unsupported expr type: vector
Error: 1004(InvalidArguments), Internal error during building DataFusion plan: No field named time. Valid fields are http_requests.ts, http_requests.job, http_requests.instance, http_requests.g, http_requests.greptime_value.
-- eval instant at 50m http_requests AND IGNORING (group, instance, job) vector(1)
-- http_requests{group="canary", instance="0", job="api-server"} 300
@@ -265,7 +265,7 @@ Error: 1004(InvalidArguments), Unsupported expr type: vector
-- NOT SUPPORTED: `vector()`
tql eval (3000, 3000, '1s') http_requests AND IGNORING (g, instance, job) vector(1);
Error: 1004(InvalidArguments), Unsupported expr type: vector
Error: 1004(InvalidArguments), Internal error during building DataFusion plan: No field named time. Valid fields are http_requests.ts, http_requests.job, http_requests.instance, http_requests.g, http_requests.greptime_value.
drop table http_requests;
@@ -280,7 +280,7 @@ drop table vector_matching_a;
Affected Rows: 0
-- the following cases are not from Prometheus.
create table t1 (ts timestamp time index, job string primary key, val double);
create table t1 (ts timestamp time index, job string primary key, greptime_value double);
Affected Rows: 0
@@ -288,7 +288,7 @@ insert into t1 values (0, "a", 1.0), (500000, "b", 2.0), (1000000, "a", 3.0), (1
Affected Rows: 4
create table t2 (ts timestamp time index, val double);
create table t2 (ts timestamp time index, greptime_value double);
Affected Rows: 0
@@ -299,102 +299,102 @@ Affected Rows: 7
-- SQLNESS SORT_RESULT 3 1
tql eval (0, 2000, '400') t1 or t2;
+-----+---------------------+-----+
| job | ts | val |
+-----+---------------------+-----+
| | 1970-01-01T00:00:00 | 0.0 |
| | 1970-01-01T00:06:40 | 0.0 |
| | 1970-01-01T00:13:20 | 0.0 |
| | 1970-01-01T00:20:00 | 0.0 |
| | 1970-01-01T00:26:40 | 0.0 |
| | 1970-01-01T00:33:20 | 0.0 |
| a | 1970-01-01T00:00:00 | 1.0 |
| a | 1970-01-01T00:20:00 | 3.0 |
| b | 1970-01-01T00:13:20 | 2.0 |
| c | 1970-01-01T00:26:40 | 4.0 |
+-----+---------------------+-----+
+---------------------+----------------+-----+
| ts | greptime_value | job |
+---------------------+----------------+-----+
| 1970-01-01T00:00:00 | 0.0 | |
| 1970-01-01T00:00:00 | 1.0 | a |
| 1970-01-01T00:06:40 | 0.0 | |
| 1970-01-01T00:13:20 | 0.0 | |
| 1970-01-01T00:13:20 | 2.0 | b |
| 1970-01-01T00:20:00 | 0.0 | |
| 1970-01-01T00:20:00 | 3.0 | a |
| 1970-01-01T00:26:40 | 0.0 | |
| 1970-01-01T00:26:40 | 4.0 | c |
| 1970-01-01T00:33:20 | 0.0 | |
+---------------------+----------------+-----+
-- SQLNESS SORT_RESULT 3 1
tql eval (0, 2000, '400') t1 or on () t2;
+-----+---------------------+-----+
| job | ts | val |
+-----+---------------------+-----+
| | 1970-01-01T00:06:40 | 0.0 |
| | 1970-01-01T00:33:20 | 0.0 |
| a | 1970-01-01T00:00:00 | 1.0 |
| a | 1970-01-01T00:20:00 | 3.0 |
| b | 1970-01-01T00:13:20 | 2.0 |
| c | 1970-01-01T00:26:40 | 4.0 |
+-----+---------------------+-----+
+---------------------+----------------+-----+
| ts | greptime_value | job |
+---------------------+----------------+-----+
| 1970-01-01T00:00:00 | 1.0 | a |
| 1970-01-01T00:06:40 | 0.0 | |
| 1970-01-01T00:13:20 | 2.0 | b |
| 1970-01-01T00:20:00 | 3.0 | a |
| 1970-01-01T00:26:40 | 4.0 | c |
| 1970-01-01T00:33:20 | 0.0 | |
+---------------------+----------------+-----+
-- SQLNESS SORT_RESULT 3 1
tql eval (0, 2000, '400') t1 or on (job) t2;
+-----+---------------------+-----+
| job | ts | val |
+-----+---------------------+-----+
| | 1970-01-01T00:00:00 | 0.0 |
| | 1970-01-01T00:06:40 | 0.0 |
| | 1970-01-01T00:13:20 | 0.0 |
| | 1970-01-01T00:20:00 | 0.0 |
| | 1970-01-01T00:26:40 | 0.0 |
| | 1970-01-01T00:33:20 | 0.0 |
| a | 1970-01-01T00:00:00 | 1.0 |
| a | 1970-01-01T00:20:00 | 3.0 |
| b | 1970-01-01T00:13:20 | 2.0 |
| c | 1970-01-01T00:26:40 | 4.0 |
+-----+---------------------+-----+
+---------------------+----------------+-----+
| ts | greptime_value | job |
+---------------------+----------------+-----+
| 1970-01-01T00:00:00 | 0.0 | |
| 1970-01-01T00:00:00 | 1.0 | a |
| 1970-01-01T00:06:40 | 0.0 | |
| 1970-01-01T00:13:20 | 0.0 | |
| 1970-01-01T00:13:20 | 2.0 | b |
| 1970-01-01T00:20:00 | 0.0 | |
| 1970-01-01T00:20:00 | 3.0 | a |
| 1970-01-01T00:26:40 | 0.0 | |
| 1970-01-01T00:26:40 | 4.0 | c |
| 1970-01-01T00:33:20 | 0.0 | |
+---------------------+----------------+-----+
-- SQLNESS SORT_RESULT 3 1
tql eval (0, 2000, '400') t2 or t1;
+-----+---------------------+-----+
| job | ts | val |
+-----+---------------------+-----+
| | 1970-01-01T00:00:00 | 0.0 |
| | 1970-01-01T00:06:40 | 0.0 |
| | 1970-01-01T00:13:20 | 0.0 |
| | 1970-01-01T00:20:00 | 0.0 |
| | 1970-01-01T00:26:40 | 0.0 |
| | 1970-01-01T00:33:20 | 0.0 |
| a | 1970-01-01T00:00:00 | 1.0 |
| a | 1970-01-01T00:20:00 | 3.0 |
| b | 1970-01-01T00:13:20 | 2.0 |
| c | 1970-01-01T00:26:40 | 4.0 |
+-----+---------------------+-----+
+---------------------+----------------+-----+
| ts | greptime_value | job |
+---------------------+----------------+-----+
| 1970-01-01T00:00:00 | 0.0 | |
| 1970-01-01T00:00:00 | 1.0 | a |
| 1970-01-01T00:06:40 | 0.0 | |
| 1970-01-01T00:13:20 | 0.0 | |
| 1970-01-01T00:13:20 | 2.0 | b |
| 1970-01-01T00:20:00 | 0.0 | |
| 1970-01-01T00:20:00 | 3.0 | a |
| 1970-01-01T00:26:40 | 0.0 | |
| 1970-01-01T00:26:40 | 4.0 | c |
| 1970-01-01T00:33:20 | 0.0 | |
+---------------------+----------------+-----+
-- SQLNESS SORT_RESULT 3 1
tql eval (0, 2000, '400') t2 or on () t1;
+-----+---------------------+-----+
| job | ts | val |
+-----+---------------------+-----+
| | 1970-01-01T00:00:00 | 0.0 |
| | 1970-01-01T00:06:40 | 0.0 |
| | 1970-01-01T00:13:20 | 0.0 |
| | 1970-01-01T00:20:00 | 0.0 |
| | 1970-01-01T00:26:40 | 0.0 |
| | 1970-01-01T00:33:20 | 0.0 |
+-----+---------------------+-----+
+---------------------+----------------+-----+
| ts | greptime_value | job |
+---------------------+----------------+-----+
| 1970-01-01T00:00:00 | 0.0 | |
| 1970-01-01T00:06:40 | 0.0 | |
| 1970-01-01T00:13:20 | 0.0 | |
| 1970-01-01T00:20:00 | 0.0 | |
| 1970-01-01T00:26:40 | 0.0 | |
| 1970-01-01T00:33:20 | 0.0 | |
+---------------------+----------------+-----+
-- SQLNESS SORT_RESULT 3 1
tql eval (0, 2000, '400') t2 or on(job) t1;
+-----+---------------------+-----+
| job | ts | val |
+-----+---------------------+-----+
| | 1970-01-01T00:00:00 | 0.0 |
| | 1970-01-01T00:06:40 | 0.0 |
| | 1970-01-01T00:13:20 | 0.0 |
| | 1970-01-01T00:20:00 | 0.0 |
| | 1970-01-01T00:26:40 | 0.0 |
| | 1970-01-01T00:33:20 | 0.0 |
| a | 1970-01-01T00:00:00 | 1.0 |
| a | 1970-01-01T00:20:00 | 3.0 |
| b | 1970-01-01T00:13:20 | 2.0 |
| c | 1970-01-01T00:26:40 | 4.0 |
+-----+---------------------+-----+
+---------------------+----------------+-----+
| ts | greptime_value | job |
+---------------------+----------------+-----+
| 1970-01-01T00:00:00 | 0.0 | |
| 1970-01-01T00:00:00 | 1.0 | a |
| 1970-01-01T00:06:40 | 0.0 | |
| 1970-01-01T00:13:20 | 0.0 | |
| 1970-01-01T00:13:20 | 2.0 | b |
| 1970-01-01T00:20:00 | 0.0 | |
| 1970-01-01T00:20:00 | 3.0 | a |
| 1970-01-01T00:26:40 | 0.0 | |
| 1970-01-01T00:26:40 | 4.0 | c |
| 1970-01-01T00:33:20 | 0.0 | |
+---------------------+----------------+-----+
drop table t1;
@@ -7,7 +7,7 @@ create table http_requests (
job string,
instance string,
g string, -- for `group`
val double,
greptime_value double,
primary key (job, instance, g)
);
@@ -27,7 +27,7 @@ create table cpu_count(ts timestamp time index);
create table vector_matching_a(
ts timestamp time index,
l string primary key,
val double,
greptime_value double,
);
insert into vector_matching_a values
@@ -176,11 +176,11 @@ drop table vector_matching_a;
-- the following cases are not from Prometheus.
create table t1 (ts timestamp time index, job string primary key, val double);
create table t1 (ts timestamp time index, job string primary key, greptime_value double);
insert into t1 values (0, "a", 1.0), (500000, "b", 2.0), (1000000, "a", 3.0), (1500000, "c", 4.0);
create table t2 (ts timestamp time index, val double);
create table t2 (ts timestamp time index, greptime_value double);
insert into t2 values (0, 0), (300000, 0), (600000, 0), (900000, 0), (1200000, 0), (1500000, 0), (1800000, 0);