fix: ignore missing columns and tables in PromQL (#6285)

* fix: handle table/column not found in or

Signed-off-by: evenyag <realevenyag@gmail.com>

* test: update result

Signed-off-by: evenyag <realevenyag@gmail.com>

* test: drop table after test

Signed-off-by: evenyag <realevenyag@gmail.com>

* test: fix test cases

Signed-off-by: evenyag <realevenyag@gmail.com>

* fix: do not return table not found error in series_query

Signed-off-by: evenyag <realevenyag@gmail.com>

---------

Signed-off-by: evenyag <realevenyag@gmail.com>
This commit is contained in:
Yingwen
2025-06-16 20:15:38 +08:00
committed by GitHub
parent 9d997d593c
commit 2a3445c72c
9 changed files with 369 additions and 91 deletions

View File

@@ -259,7 +259,11 @@ impl ExecutionPlan for EmptyMetricExec {
}
fn statistics(&self) -> DataFusionResult<Statistics> {
let estimated_row_num = (self.end - self.start) as f64 / self.interval as f64;
let estimated_row_num = if self.end > self.start {
(self.end - self.start) as f64 / self.interval as f64
} else {
0.0
};
let total_byte_size = estimated_row_num * std::mem::size_of::<Millisecond>() as f64;
Ok(Statistics {

View File

@@ -19,6 +19,8 @@ use std::time::UNIX_EPOCH;
use arrow::datatypes::IntervalDayTime;
use async_recursion::async_recursion;
use catalog::table_source::DfTableSourceProvider;
use common_error::ext::ErrorExt;
use common_error::status_code::StatusCode;
use common_query::prelude::GREPTIME_VALUE;
use datafusion::common::DFSchemaRef;
use datafusion::datasource::DefaultTableSource;
@@ -216,6 +218,7 @@ impl PromPlanner {
PromExpr::Call(expr) => self.prom_call_expr_to_plan(session_state, expr).await?,
PromExpr::Extension(expr) => self.prom_ext_expr_to_plan(session_state, expr).await?,
};
Ok(res)
}
@@ -274,6 +277,7 @@ impl PromPlanner {
} = aggr_expr;
let input = self.prom_expr_to_plan(expr, session_state).await?;
match (*op).id() {
token::T_TOPK | token::T_BOTTOMK => {
self.prom_topk_bottomk_to_plan(aggr_expr, input).await
@@ -677,7 +681,9 @@ impl PromPlanner {
at: _,
} = vector_selector;
let matchers = self.preprocess_label_matchers(matchers, name)?;
self.setup_context().await?;
if let Some(empty_plan) = self.setup_context().await? {
return Ok(empty_plan);
}
let normalize = self
.selector_to_series_normalize_plan(offset, matchers, false)
.await?;
@@ -710,7 +716,9 @@ impl PromPlanner {
..
} = vs;
let matchers = self.preprocess_label_matchers(matchers, name)?;
self.setup_context().await?;
if let Some(empty_plan) = self.setup_context().await? {
return Ok(empty_plan);
}
ensure!(!range.is_zero(), ZeroRangeSelectorSnafu);
let range_ms = range.as_millis() as _;
@@ -1089,24 +1097,26 @@ impl PromPlanner {
match modifier {
None => {
if update_ctx {
self.ctx.tag_columns = vec![];
self.ctx.tag_columns.clear();
}
Ok(vec![self.create_time_index_column_expr()?])
}
Some(LabelModifier::Include(labels)) => {
if update_ctx {
self.ctx.tag_columns.clear();
}
let mut exprs = Vec::with_capacity(labels.labels.len());
for label in &labels.labels {
// nonexistence label will be ignored
if let Ok(field) = input_schema.field_with_unqualified_name(label) {
exprs.push(DfExpr::Column(Column::from(field.name())));
if update_ctx {
// update the tag columns in context
self.ctx.tag_columns.push(label.clone());
}
}
}
if update_ctx {
// change the tag columns in context
self.ctx.tag_columns.clone_from(&labels.labels);
}
// add timestamp column
exprs.push(self.create_time_index_column_expr()?);
@@ -1337,13 +1347,18 @@ impl PromPlanner {
}
/// Setup [PromPlannerContext]'s state fields.
async fn setup_context(&mut self) -> Result<()> {
///
/// Returns a logical plan for an empty metric.
async fn setup_context(&mut self) -> Result<Option<LogicalPlan>> {
let table_ref = self.table_ref()?;
let table = self
.table_provider
.resolve_table(table_ref.clone())
.await
.context(CatalogSnafu)?
let table = match self.table_provider.resolve_table(table_ref.clone()).await {
Err(e) if e.status_code() == StatusCode::TableNotFound => {
let plan = self.setup_context_for_empty_metric()?;
return Ok(Some(plan));
}
res => res.context(CatalogSnafu)?,
};
let table = table
.as_any()
.downcast_ref::<DefaultTableSource>()
.context(UnknownTableSnafu)?
@@ -1386,7 +1401,32 @@ impl PromPlanner {
.collect();
self.ctx.tag_columns = tags;
Ok(())
Ok(None)
}
/// Setup [PromPlannerContext]'s state fields for a non existent table
/// without any rows.
fn setup_context_for_empty_metric(&mut self) -> Result<LogicalPlan> {
self.ctx.time_index_column = Some(SPECIAL_TIME_FUNCTION.to_string());
self.ctx.reset_table_name_and_schema();
self.ctx.tag_columns = vec![];
self.ctx.field_columns = vec![DEFAULT_FIELD_COLUMN.to_string()];
// The table doesn't have any data, so we set start to 0 and end to -1.
let plan = LogicalPlan::Extension(Extension {
node: Arc::new(
EmptyMetric::new(
0,
-1,
self.ctx.interval,
SPECIAL_TIME_FUNCTION.to_string(),
DEFAULT_FIELD_COLUMN.to_string(),
Some(DfExpr::Literal(ScalarValue::Float64(Some(0.0)))),
)
.context(DataFusionPlanningSnafu)?,
),
});
Ok(plan)
}
// TODO(ruihang): insert column expr
@@ -2767,11 +2807,12 @@ impl PromPlanner {
col: right_field_col.to_string(),
})?
.cloned();
// `skip1)` to skip the time index column
let right_proj_exprs_without_time_index = all_columns.iter().skip(1).map(|col| {
// expr
if col == left_field_col && left_field_col != right_field_col {
// alias field in right side if necessary to handle different field name
// qualify field in right side if necessary to handle different field name
DfExpr::Column(Column::new(
right_qualifier_for_field.clone(),
right_field_col,
@@ -3828,10 +3869,10 @@ mod test {
};
let case = r#"
sum(rate(sysstat{tenant_name=~"tenant1",cluster_name=~"cluster1"}[120s])) by (cluster_name,tenant_name) /
sum(rate(sysstat{tenant_name=~"tenant1",cluster_name=~"cluster1"}[120s])) by (cluster_name,tenant_name) /
(sum(sysstat{tenant_name=~"tenant1",cluster_name=~"cluster1"}) by (cluster_name,tenant_name) * 100)
or
200 * sum(sysstat{tenant_name=~"tenant1",cluster_name=~"cluster1"}) by (cluster_name,tenant_name) /
200 * sum(sysstat{tenant_name=~"tenant1",cluster_name=~"cluster1"}) by (cluster_name,tenant_name) /
sum(sysstat{tenant_name=~"tenant1",cluster_name=~"cluster1"}) by (cluster_name,tenant_name)"#;
let table_provider = build_test_table_provider_with_fields(
@@ -3844,15 +3885,15 @@ mod test {
.await
.unwrap();
let case = r#"sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
let case = r#"sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
(sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) *1000) +
sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
(sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) *1000) >= 0
or
sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
(sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) *1000) >= 0
or
sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
(sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) *1000) >= 0
or
sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
(sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) *1000) >= 0
or
sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) /
(sum(delta(sysstat{tenant_name=~"sys",cluster_name=~"cluster1"}[2m])/120) by (cluster_name,tenant_name) *1000) >= 0"#;
let table_provider = build_test_table_provider_with_fields(
&[(DEFAULT_SCHEMA_NAME.to_string(), "sysstat".to_string())],
@@ -3864,9 +3905,9 @@ mod test {
.await
.unwrap();
let case = r#"(sum(background_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name) +
sum(foreground_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name)) or
(sum(background_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name)) or
let case = r#"(sum(background_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name) +
sum(foreground_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name)) or
(sum(background_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name)) or
(sum(foreground_waitevent_cnt{tenant_name=~"sys",cluster_name=~"cluster1"}) by (cluster_name,tenant_name))"#;
let table_provider = build_test_table_provider_with_fields(
&[
@@ -4519,4 +4560,47 @@ mod test {
assert_eq!(plan.display_indent_schema().to_string(), expected);
}
#[tokio::test]
async fn test_or_not_exists_table_label() {
let mut eval_stmt = EvalStmt {
expr: PromExpr::NumberLiteral(NumberLiteral { val: 1.0 }),
start: UNIX_EPOCH,
end: UNIX_EPOCH
.checked_add(Duration::from_secs(100_000))
.unwrap(),
interval: Duration::from_secs(5),
lookback_delta: Duration::from_secs(1),
};
let case = r#"sum by (job, tag0, tag2) (metric_exists) or sum by (job, tag0, tag2) (metric_not_exists)"#;
let prom_expr = parser::parse(case).unwrap();
eval_stmt.expr = prom_expr;
let table_provider = build_test_table_provider_with_fields(
&[(DEFAULT_SCHEMA_NAME.to_string(), "metric_exists".to_string())],
&["job"],
)
.await;
let plan = PromPlanner::stmt_to_plan(table_provider, &eval_stmt, &build_session_state())
.await
.unwrap();
let expected = "UnionDistinctOn: on col=[[\"job\"]], ts_col=[greptime_timestamp] [greptime_timestamp:Timestamp(Millisecond, None), job:Utf8, sum(metric_exists.greptime_value):Float64;N]\
\n SubqueryAlias: metric_exists [greptime_timestamp:Timestamp(Millisecond, None), job:Utf8, sum(metric_exists.greptime_value):Float64;N]\
\n Projection: metric_exists.greptime_timestamp, metric_exists.job, sum(metric_exists.greptime_value) [greptime_timestamp:Timestamp(Millisecond, None), job:Utf8, sum(metric_exists.greptime_value):Float64;N]\
\n Sort: metric_exists.job ASC NULLS LAST, metric_exists.greptime_timestamp ASC NULLS LAST [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), sum(metric_exists.greptime_value):Float64;N]\
\n Aggregate: groupBy=[[metric_exists.job, metric_exists.greptime_timestamp]], aggr=[[sum(metric_exists.greptime_value)]] [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), sum(metric_exists.greptime_value):Float64;N]\
\n PromInstantManipulate: range=[0..100000000], lookback=[1000], interval=[5000], time index=[greptime_timestamp] [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), greptime_value:Float64;N]\
\n PromSeriesDivide: tags=[\"job\"] [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), greptime_value:Float64;N]\
\n Sort: metric_exists.job ASC NULLS FIRST, metric_exists.greptime_timestamp ASC NULLS FIRST [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), greptime_value:Float64;N]\
\n Filter: metric_exists.greptime_timestamp >= TimestampMillisecond(-1000, None) AND metric_exists.greptime_timestamp <= TimestampMillisecond(100001000, None) [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), greptime_value:Float64;N]\
\n TableScan: metric_exists [job:Utf8, greptime_timestamp:Timestamp(Millisecond, None), greptime_value:Float64;N]\
\n SubqueryAlias: [greptime_timestamp:Timestamp(Millisecond, None), job:Utf8;N, sum(.value):Float64;N]\
\n Projection: .time AS greptime_timestamp, Utf8(NULL) AS job, sum(.value) [greptime_timestamp:Timestamp(Millisecond, None), job:Utf8;N, sum(.value):Float64;N]\
\n Sort: .time ASC NULLS LAST [time:Timestamp(Millisecond, None), sum(.value):Float64;N]\
\n Aggregate: groupBy=[[.time]], aggr=[[sum(.value)]] [time:Timestamp(Millisecond, None), sum(.value):Float64;N]\
\n EmptyMetric: range=[0..-1], interval=[5000] [time:Timestamp(Millisecond, None), value:Float64;N]";
assert_eq!(plan.display_indent_schema().to_string(), expected);
}
}

View File

@@ -478,6 +478,30 @@ impl<'de> Deserialize<'de> for Matches {
}
}
/// Handles schema errors, transforming a Result into an Option.
/// - If the input is `Ok(v)`, returns `Some(v)`
/// - If the input is `Err(err)` and the error status code is `TableNotFound` or
/// `TableColumnNotFound`, returns `None` (ignoring these specific errors)
/// - If the input is `Err(err)` with any other error code, directly returns a
/// `PrometheusJsonResponse::error`.
macro_rules! handle_schema_err {
($result:expr) => {
match $result {
Ok(v) => Some(v),
Err(err) => {
if err.status_code() == StatusCode::TableNotFound
|| err.status_code() == StatusCode::TableColumnNotFound
{
// Prometheus won't report error if querying nonexist label and metric
None
} else {
return PrometheusJsonResponse::error(err.status_code(), err.output_msg());
}
}
}
};
}
#[axum_macros::debug_handler]
#[tracing::instrument(
skip_all,
@@ -546,17 +570,10 @@ pub async fn labels_query(
};
let result = handler.do_query(&prom_query, query_ctx.clone()).await;
if let Err(err) =
handle_schema_err!(
retrieve_labels_name_from_query_result(result, &mut fetched_labels, &mut merge_map)
.await
{
// Prometheus won't report error if querying nonexist label and metric
if err.status_code() != StatusCode::TableNotFound
&& err.status_code() != StatusCode::TableColumnNotFound
{
return PrometheusJsonResponse::error(err.status_code(), err.output_msg());
}
}
);
}
// intersect `fetched_labels` with `labels` to filter out non-tag columns
@@ -971,15 +988,10 @@ pub async fn label_values_query(
table_names.sort_unstable();
return PrometheusJsonResponse::success(PrometheusResponse::LabelValues(table_names));
} else if label_name == FIELD_NAME_LABEL {
let field_columns =
match retrieve_field_names(&query_ctx, handler.catalog_manager(), params.matches.0)
.await
{
Ok(table_names) => table_names,
Err(e) => {
return PrometheusJsonResponse::error(e.status_code(), e.output_msg());
}
};
let field_columns = handle_schema_err!(
retrieve_field_names(&query_ctx, handler.catalog_manager(), params.matches.0).await
)
.unwrap_or_default();
let mut field_columns = field_columns.into_iter().collect::<Vec<_>>();
field_columns.sort_unstable();
return PrometheusJsonResponse::success(PrometheusResponse::LabelValues(field_columns));
@@ -1029,19 +1041,8 @@ pub async fn label_values_query(
&query_ctx,
)
.await;
match result {
Ok(result) => {
label_values.extend(result.into_iter());
}
Err(err) => {
// Prometheus won't report error if querying nonexist label and metric
if err.status_code() != StatusCode::TableNotFound
&& err.status_code() != StatusCode::TableColumnNotFound
{
return PrometheusJsonResponse::error(err.status_code(), err.output_msg());
}
}
if let Some(result) = handle_schema_err!(result) {
label_values.extend(result.into_iter());
}
}
@@ -1229,18 +1230,17 @@ pub async fn series_query(
};
let result = handler.do_query(&prom_query, query_ctx.clone()).await;
if let Err(err) = retrieve_series_from_query_result(
result,
&mut series,
&query_ctx,
&table_name,
&handler.catalog_manager(),
&mut merge_map,
)
.await
{
return PrometheusJsonResponse::error(err.status_code(), err.output_msg());
}
handle_schema_err!(
retrieve_series_from_query_result(
result,
&mut series,
&query_ctx,
&table_name,
&handler.catalog_manager(),
&mut merge_map,
)
.await
);
}
let merge_map = merge_map
.into_iter()

View File

@@ -638,8 +638,13 @@ async fn cross_schema_query(instance: Arc<dyn MockInstance>) {
interval,
lookback_delta,
)
.await;
assert!(query_output.is_err());
.await
.unwrap();
let empty_result = r#"+------+-------+
| time | value |
+------+-------+
+------+-------+"#;
check_unordered_output_stream(query_output, empty_result).await;
let query_output = promql_query(
ins.clone(),

View File

@@ -666,15 +666,10 @@ pub async fn test_prom_http_api(store_type: StorageType) {
.header("Content-Type", "application/x-www-form-urlencoded")
.send()
.await;
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
let prom_resp = res.json::<PrometheusJsonResponse>().await;
assert_eq!(prom_resp.status, "error");
assert!(prom_resp
.error_type
.is_some_and(|err| err.eq_ignore_ascii_case("TableNotFound")));
assert!(prom_resp
.error
.is_some_and(|err| err.eq_ignore_ascii_case("Table not found: greptime.public.up")));
assert_eq!(res.status(), StatusCode::OK);
// An empty array will be deserialized into PrometheusResponse::Labels.
// So here we compare the text directly.
assert_eq!(res.text().await, r#"{"status":"success","data":[]}"#);
// label values
// should return error if there is no match[]

View File

@@ -12,7 +12,7 @@ create table http_requests (
Affected Rows: 0
insert into http_requests values
insert into http_requests values
(3000000, "api", "0", "production", 100),
(3000000, "api", "1", "production", 200),
(3000000, "api", "0", "canary", 300),
@@ -718,3 +718,125 @@ drop table cache_miss_with_null_label;
Affected Rows: 0
-- Physical table for test
CREATE TABLE test_physical (greptime_timestamp timestamp time index, greptime_value double) engine=metric with ("physical_metric_table" = "");
Affected Rows: 0
CREATE TABLE IF NOT EXISTS node_network_transmit_bytes_total (
cloud STRING NULL,
dest_port STRING NULL,
dest STRING NULL,
greptime_timestamp TIMESTAMP(3) NOT NULL,
greptime_value DOUBLE NULL,
host STRING NULL,
job STRING NULL,
node STRING NULL,
region STRING NULL,
src_port STRING NULL,
src STRING NULL,
src_namespace STRING NULL,
src_node STRING NULL,
src_pod STRING NULL,
az STRING NULL,
TIME INDEX (greptime_timestamp),
PRIMARY KEY (cloud, dest_port, dest, host, job, node, region, src_port, src, src_namespace, src_node, src_pod, az)
)
ENGINE=metric
WITH(
on_physical_table = 'test_physical'
);
Affected Rows: 0
INSERT INTO node_network_transmit_bytes_total (
cloud, dest_port, dest, greptime_timestamp, greptime_value, host, job, node, region, src_port, src, src_namespace, src_node, src_pod, az
) VALUES
('cloud-1', '443', '10.0.0.2', 1000000, 1000, 'host-1', 'greptimedb', 'node-a', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-1', 'pod-1', 'us-west-6'),
('cloud-1', '443', '10.0.0.2', 1200000, 2000, 'host-1', 'greptimedb', 'node-a', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-1', 'pod-1', 'us-west-6'),
('cloud-1', '443', '10.0.0.3', 1000000, 1500, 'host-2', 'greptimedb', 'node-b', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-2', 'pod-2', 'us-west-6'),
('cloud-1', '443', '10.0.0.3', 1200000, 2500, 'host-2', 'greptimedb', 'node-b', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-2', 'pod-2', 'us-west-6'),
('cloud-2', '80', '10.0.0.5', 1000000, 800, 'host-3', 'greptimedb', 'node-c', 'us-west', '9000', '10.0.0.4', 'namespace-2', 'node-3', 'pod-3', 'us-west-6'),
('cloud-2', '80', '10.0.0.5', 1200000, 1800, 'host-3', 'greptimedb', 'node-c', 'us-west', '9000', '10.0.0.4', 'namespace-2', 'node-3', 'pod-3', 'us-west-6');
Affected Rows: 6
-- Or with unknown label and metric.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') unknown_metric or node_network_transmit_bytes_total;
+---------------------+-----------+---------+----------+-----------+--------+------------+--------+---------+----------+---------------+----------+---------+----------+--------+
| time | az | cloud | dest | dest_port | host | job | node | region | src | src_namespace | src_node | src_pod | src_port | value |
+---------------------+-----------+---------+----------+-----------+--------+------------+--------+---------+----------+---------------+----------+---------+----------+--------+
| 1970-01-01T00:16:40 | us-west-6 | cloud-1 | 10.0.0.2 | 443 | host-1 | greptimedb | node-a | us-west | 10.0.0.1 | namespace-1 | node-1 | pod-1 | 8080 | 1000.0 |
| 1970-01-01T00:16:40 | us-west-6 | cloud-1 | 10.0.0.3 | 443 | host-2 | greptimedb | node-b | us-west | 10.0.0.1 | namespace-1 | node-2 | pod-2 | 8080 | 1500.0 |
| 1970-01-01T00:16:40 | us-west-6 | cloud-2 | 10.0.0.5 | 80 | host-3 | greptimedb | node-c | us-west | 10.0.0.4 | namespace-2 | node-3 | pod-3 | 9000 | 800.0 |
| 1970-01-01T00:21:40 | us-west-6 | cloud-1 | 10.0.0.2 | 443 | host-1 | greptimedb | node-a | us-west | 10.0.0.1 | namespace-1 | node-1 | pod-1 | 8080 | 2000.0 |
| 1970-01-01T00:21:40 | us-west-6 | cloud-1 | 10.0.0.3 | 443 | host-2 | greptimedb | node-b | us-west | 10.0.0.1 | namespace-1 | node-2 | pod-2 | 8080 | 2500.0 |
| 1970-01-01T00:21:40 | us-west-6 | cloud-2 | 10.0.0.5 | 80 | host-3 | greptimedb | node-c | us-west | 10.0.0.4 | namespace-2 | node-3 | pod-3 | 9000 | 1800.0 |
+---------------------+-----------+---------+----------+-----------+--------+------------+--------+---------+----------+---------------+----------+---------+----------+--------+
-- Or with unknown label and metric.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') sum by (cloud, tag0, tag1) (node_network_transmit_bytes_total) or unknown_metric;
+---------------------+---------+-------------------------------------------------------+
| greptime_timestamp | cloud | sum(node_network_transmit_bytes_total.greptime_value) |
+---------------------+---------+-------------------------------------------------------+
| 1970-01-01T00:16:40 | cloud-1 | 2500.0 |
| 1970-01-01T00:16:40 | cloud-2 | 800.0 |
| 1970-01-01T00:21:40 | cloud-1 | 4500.0 |
| 1970-01-01T00:21:40 | cloud-2 | 1800.0 |
+---------------------+---------+-------------------------------------------------------+
-- Or with unknown label and metric.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') unknown_metric or unknown_metric1 or sum by (cloud, tag0, tag1) (node_network_transmit_bytes_total);
+---------------------+---------+--------+
| time | cloud | value |
+---------------------+---------+--------+
| 1970-01-01T00:16:40 | cloud-1 | 2500.0 |
| 1970-01-01T00:16:40 | cloud-2 | 800.0 |
| 1970-01-01T00:21:40 | cloud-1 | 4500.0 |
| 1970-01-01T00:21:40 | cloud-2 | 1800.0 |
+---------------------+---------+--------+
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') sum by (cloud, tag0, tag1) (node_network_transmit_bytes_total) or sum by (cloud, tag0, tag1) (unknown_metric);
+---------------------+---------+-------------------------------------------------------+
| greptime_timestamp | cloud | sum(node_network_transmit_bytes_total.greptime_value) |
+---------------------+---------+-------------------------------------------------------+
| 1970-01-01T00:16:40 | cloud-1 | 2500.0 |
| 1970-01-01T00:16:40 | cloud-2 | 800.0 |
| 1970-01-01T00:21:40 | cloud-1 | 4500.0 |
| 1970-01-01T00:21:40 | cloud-2 | 1800.0 |
+---------------------+---------+-------------------------------------------------------+
-- Or with unknown label dst_namespace.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') sum by (src, src_pod, src_namespace, src_node, dest, dst_pod, dst_namespace, dst_node, cloud, region, az) (increase(node_network_transmit_bytes_total{src_node!="", job=~"greptimedb", region=~"us-west", az=~"us-west-6"}[900s])>0) or sum by (src, src_pod, src_namespace, src_node, dest, dst_pod, dst_namespace, dst_node, cloud, region, az) (increase(node_network_transmit_bytes_total{dst_node!="", job=~"greptimedb", region=~"us-west", az=~"us-west-6"}[900s])>0);
+---------------------+-----------+---------+----------+---------+----------+---------------+----------+---------+----------------------------------------------------------------------------------------------+
| greptime_timestamp | az | cloud | dest | region | src | src_namespace | src_node | src_pod | sum(prom_increase(greptime_timestamp_range,greptime_value,greptime_timestamp,Int64(900000))) |
+---------------------+-----------+---------+----------+---------+----------+---------------+----------+---------+----------------------------------------------------------------------------------------------+
| 1970-01-01T00:21:40 | us-west-6 | cloud-1 | 10.0.0.2 | us-west | 10.0.0.1 | namespace-1 | node-1 | pod-1 | 2500.0 |
| 1970-01-01T00:21:40 | us-west-6 | cloud-1 | 10.0.0.3 | us-west | 10.0.0.1 | namespace-1 | node-2 | pod-2 | 2000.0 |
| 1970-01-01T00:21:40 | us-west-6 | cloud-2 | 10.0.0.5 | us-west | 10.0.0.4 | namespace-2 | node-3 | pod-3 | 2300.0 |
| 1970-01-01T00:26:40 | us-west-6 | cloud-1 | 10.0.0.2 | us-west | 10.0.0.1 | namespace-1 | node-1 | pod-1 | 2500.0 |
| 1970-01-01T00:26:40 | us-west-6 | cloud-1 | 10.0.0.3 | us-west | 10.0.0.1 | namespace-1 | node-2 | pod-2 | 2000.0 |
| 1970-01-01T00:26:40 | us-west-6 | cloud-2 | 10.0.0.5 | us-west | 10.0.0.4 | namespace-2 | node-3 | pod-3 | 2300.0 |
| 1970-01-01T00:31:40 | us-west-6 | cloud-1 | 10.0.0.2 | us-west | 10.0.0.1 | namespace-1 | node-1 | pod-1 | 1500.0 |
| 1970-01-01T00:31:40 | us-west-6 | cloud-1 | 10.0.0.3 | us-west | 10.0.0.1 | namespace-1 | node-2 | pod-2 | 1500.0 |
| 1970-01-01T00:31:40 | us-west-6 | cloud-2 | 10.0.0.5 | us-west | 10.0.0.4 | namespace-2 | node-3 | pod-3 | 1500.0 |
+---------------------+-----------+---------+----------+---------+----------+---------------+----------+---------+----------------------------------------------------------------------------------------------+
DROP TABLE node_network_transmit_bytes_total;
Affected Rows: 0
DROP TABLE test_physical;
Affected Rows: 0

View File

@@ -11,7 +11,7 @@ create table http_requests (
primary key (job, instance, g)
);
insert into http_requests values
insert into http_requests values
(3000000, "api", "0", "production", 100),
(3000000, "api", "1", "production", 200),
(3000000, "api", "0", "canary", 300),
@@ -336,3 +336,63 @@ tql eval (3, 4, '1s') cache_hit_with_null_label / on(job) (cache_miss_with_null_
drop table cache_hit_with_null_label;
drop table cache_miss_with_null_label;
-- Physical table for test
CREATE TABLE test_physical (greptime_timestamp timestamp time index, greptime_value double) engine=metric with ("physical_metric_table" = "");
CREATE TABLE IF NOT EXISTS node_network_transmit_bytes_total (
cloud STRING NULL,
dest_port STRING NULL,
dest STRING NULL,
greptime_timestamp TIMESTAMP(3) NOT NULL,
greptime_value DOUBLE NULL,
host STRING NULL,
job STRING NULL,
node STRING NULL,
region STRING NULL,
src_port STRING NULL,
src STRING NULL,
src_namespace STRING NULL,
src_node STRING NULL,
src_pod STRING NULL,
az STRING NULL,
TIME INDEX (greptime_timestamp),
PRIMARY KEY (cloud, dest_port, dest, host, job, node, region, src_port, src, src_namespace, src_node, src_pod, az)
)
ENGINE=metric
WITH(
on_physical_table = 'test_physical'
);
INSERT INTO node_network_transmit_bytes_total (
cloud, dest_port, dest, greptime_timestamp, greptime_value, host, job, node, region, src_port, src, src_namespace, src_node, src_pod, az
) VALUES
('cloud-1', '443', '10.0.0.2', 1000000, 1000, 'host-1', 'greptimedb', 'node-a', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-1', 'pod-1', 'us-west-6'),
('cloud-1', '443', '10.0.0.2', 1200000, 2000, 'host-1', 'greptimedb', 'node-a', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-1', 'pod-1', 'us-west-6'),
('cloud-1', '443', '10.0.0.3', 1000000, 1500, 'host-2', 'greptimedb', 'node-b', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-2', 'pod-2', 'us-west-6'),
('cloud-1', '443', '10.0.0.3', 1200000, 2500, 'host-2', 'greptimedb', 'node-b', 'us-west', '8080', '10.0.0.1', 'namespace-1', 'node-2', 'pod-2', 'us-west-6'),
('cloud-2', '80', '10.0.0.5', 1000000, 800, 'host-3', 'greptimedb', 'node-c', 'us-west', '9000', '10.0.0.4', 'namespace-2', 'node-3', 'pod-3', 'us-west-6'),
('cloud-2', '80', '10.0.0.5', 1200000, 1800, 'host-3', 'greptimedb', 'node-c', 'us-west', '9000', '10.0.0.4', 'namespace-2', 'node-3', 'pod-3', 'us-west-6');
-- Or with unknown label and metric.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') unknown_metric or node_network_transmit_bytes_total;
-- Or with unknown label and metric.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') sum by (cloud, tag0, tag1) (node_network_transmit_bytes_total) or unknown_metric;
-- Or with unknown label and metric.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') unknown_metric or unknown_metric1 or sum by (cloud, tag0, tag1) (node_network_transmit_bytes_total);
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') sum by (cloud, tag0, tag1) (node_network_transmit_bytes_total) or sum by (cloud, tag0, tag1) (unknown_metric);
-- Or with unknown label dst_namespace.
-- SQLNESS SORT_RESULT 3 1
tql eval(1000, 2000, '300s') sum by (src, src_pod, src_namespace, src_node, dest, dst_pod, dst_namespace, dst_node, cloud, region, az) (increase(node_network_transmit_bytes_total{src_node!="", job=~"greptimedb", region=~"us-west", az=~"us-west-6"}[900s])>0) or sum by (src, src_pod, src_namespace, src_node, dest, dst_pod, dst_namespace, dst_node, cloud, region, az) (increase(node_network_transmit_bytes_total{dst_node!="", job=~"greptimedb", region=~"us-west", az=~"us-west-6"}[900s])>0);
DROP TABLE node_network_transmit_bytes_total;
DROP TABLE test_physical;

View File

@@ -47,7 +47,10 @@ TQL EVAL (0, 10, '5s') test{__schema__="public"};
-- SQLNESS SORT_RESULT 2 1
TQL EVAL (0, 10, '5s') test{__schema__="greptime_private"};
Error: 4001(TableNotFound), Table not found: greptime.greptime_private.test
+------+-------+
| time | value |
+------+-------+
+------+-------+
-- SQLNESS SORT_RESULT 2 1
TQL EVAL (0, 10, '5s') test{__database__="public"};
@@ -64,7 +67,10 @@ TQL EVAL (0, 10, '5s') test{__database__="public"};
-- SQLNESS SORT_RESULT 2 1
TQL EVAL (0, 10, '5s') test{__database__="greptime_private"};
Error: 4001(TableNotFound), Table not found: greptime.greptime_private.test
+------+-------+
| time | value |
+------+-------+
+------+-------+
-- SQLNESS SORT_RESULT 2 1
TQL EVAL (0, 10, '5s') {__name__="test", __field__="i"};

View File

@@ -60,12 +60,14 @@ Affected Rows: 0
tql eval (0,10,'5s') sum(MemAvailable / 4) + sum(MemTotal / 4);
Error: 4001(TableNotFound), Table not found: greptime.public.MemTotal
++
++
-- Cross schema is not supported
tql eval (0,10,'5s') sum(MemAvailable / 4) + sum({__name__="AnotherSchema.MemTotal"} / 4);
Error: 4001(TableNotFound), Table not found: greptime.public.AnotherSchema.MemTotal
++
++
drop table "MemAvailable";