diff --git a/src/catalog/src/process_manager.rs b/src/catalog/src/process_manager.rs index 1eebfec627..8796d948e1 100644 --- a/src/catalog/src/process_manager.rs +++ b/src/catalog/src/process_manager.rs @@ -395,7 +395,7 @@ impl SlowQueryTimer { impl Drop for SlowQueryTimer { fn drop(&mut self) { - // Calculate the elaspsed duration since the timer is created. + // Calculate the elapsed duration since the timer is created. let elapsed = self.start.elapsed(); if elapsed > self.threshold { // Only capture a portion of slow queries based on sample_ratio. diff --git a/src/frontend/src/instance/promql.rs b/src/frontend/src/instance/promql.rs index 419be8d96e..3a3aba2307 100644 --- a/src/frontend/src/instance/promql.rs +++ b/src/frontend/src/instance/promql.rs @@ -31,7 +31,7 @@ use snafu::{OptionExt, ResultExt}; use crate::error::{ CatalogSnafu, CollectRecordbatchSnafu, ExecLogicalPlanSnafu, PrometheusLabelValuesQueryPlanSnafu, PrometheusMetricNamesQueryPlanSnafu, ReadTableSnafu, - Result, TableNotFoundSnafu, + Result, TableNotFoundSnafu, TableSnafu, }; use crate::instance::Instance; @@ -120,20 +120,32 @@ impl Instance { }) .unwrap_or_else(|| ctx.current_schema()); + let full_table_name = format_full_table_name(ctx.current_catalog(), &table_schema, &metric); let table = self .catalog_manager .table(ctx.current_catalog(), &table_schema, &metric, Some(ctx)) .await .context(CatalogSnafu)? .with_context(|| TableNotFoundSnafu { - table_name: format_full_table_name(ctx.current_catalog(), &table_schema, &metric), + table_name: full_table_name.clone(), })?; + // Check label column existence before building the query plan so a missing label can be + // reported as `TableColumnNotFound` and handled like Prometheus expects. + if table.schema().column_schema_by_name(&label_name).is_none() { + return table::error::ColumnNotExistsSnafu { + column_name: label_name, + table_name: full_table_name, + } + .fail() + .context(TableSnafu); + } + let dataframe = self .query_engine .read_table(table.clone()) .with_context(|_| ReadTableSnafu { - table_name: format_full_table_name(ctx.current_catalog(), &table_schema, &metric), + table_name: full_table_name, })?; let scan_plan = dataframe.into_unoptimized_plan(); diff --git a/tests-integration/tests/http.rs b/tests-integration/tests/http.rs index 36ddb1bb38..caf5b2d11c 100644 --- a/tests-integration/tests/http.rs +++ b/tests-integration/tests/http.rs @@ -1176,6 +1176,20 @@ pub async fn test_prom_http_api(store_type: StorageType) { .await; assert_eq!(res.status(), StatusCode::OK); + // query non-exist label in metric table + let res = client + .get("/v1/prometheus/api/v1/label/not_exist_label/values?match[]=demo&start=0&end=600") + .send() + .await; + assert_eq!(res.status(), StatusCode::OK); + let prom_resp = res.json::().await; + assert_eq!(prom_resp.status, "success"); + assert!(prom_resp.error.is_none() && prom_resp.error_type.is_none()); + assert_eq!( + prom_resp.data, + serde_json::from_value::(json!([])).unwrap() + ); + // query `__name__` without match[] // create a physical table and a logical table let res = client