Skip to main content

servers/http/
prometheus.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! prom supply the prometheus HTTP API Server compliance
16
17use std::borrow::Borrow;
18use std::collections::{BTreeMap, HashMap, HashSet};
19use std::hash::{Hash, Hasher};
20use std::sync::Arc;
21
22use arrow::array::{Array, AsArray};
23use arrow::datatypes::{
24    Date32Type, Date64Type, Decimal128Type, Float32Type, Float64Type, Int8Type, Int16Type,
25    Int32Type, Int64Type, IntervalDayTimeType, IntervalMonthDayNanoType, IntervalYearMonthType,
26    UInt8Type, UInt16Type, UInt32Type, UInt64Type,
27};
28use arrow_schema::{DataType, IntervalUnit};
29use axum::extract::{Path, Query, State};
30use axum::{Extension, Form};
31use catalog::CatalogManagerRef;
32use common_catalog::parse_catalog_and_schema_from_db_string;
33use common_decimal::Decimal128;
34use common_error::ext::ErrorExt;
35use common_error::status_code::StatusCode;
36use common_query::{Output, OutputData};
37use common_recordbatch::{RecordBatch, RecordBatches};
38use common_telemetry::{debug, tracing};
39use common_time::util::{current_time_rfc3339, yesterday_rfc3339};
40use common_time::{Date, IntervalDayTime, IntervalMonthDayNano, IntervalYearMonth};
41use common_version::OwnedBuildInfo;
42use datafusion_common::ScalarValue;
43use datatypes::prelude::ConcreteDataType;
44use datatypes::schema::{ColumnSchema, SchemaRef};
45use datatypes::types::jsonb_to_string;
46use futures::future::join_all;
47use futures::{StreamExt, TryStreamExt};
48use itertools::Itertools;
49use promql_parser::label::{METRIC_NAME, MatchOp, Matcher, Matchers};
50use promql_parser::parser::token::{self};
51use promql_parser::parser::value::ValueType;
52use promql_parser::parser::{
53    AggregateExpr, BinaryExpr, Call, Expr as PromqlExpr, LabelModifier, MatrixSelector, ParenExpr,
54    SubqueryExpr, UnaryExpr, VectorSelector,
55};
56use query::parser::{DEFAULT_LOOKBACK_STRING, PromQuery, QueryLanguageParser};
57use serde::de::{self, MapAccess, Visitor};
58use serde::{Deserialize, Serialize};
59use serde_json::Value;
60use session::context::{QueryContext, QueryContextRef};
61use snafu::{Location, OptionExt, ResultExt};
62use store_api::metric_engine_consts::{
63    DATA_SCHEMA_TABLE_ID_COLUMN_NAME, DATA_SCHEMA_TSID_COLUMN_NAME, LOGICAL_TABLE_METADATA_KEY,
64};
65
66pub use super::result::prometheus_resp::PrometheusJsonResponse;
67use crate::error::{
68    CollectRecordbatchSnafu, ConvertScalarValueSnafu, DataFusionSnafu, Error, InvalidQuerySnafu,
69    NotSupportedSnafu, ParseTimestampSnafu, Result, TableNotFoundSnafu, UnexpectedResultSnafu,
70};
71use crate::http::header::collect_plan_metrics;
72use crate::prom_store::{FIELD_NAME_LABEL, METRIC_NAME_LABEL, is_database_selection_label};
73use crate::prometheus_handler::PrometheusHandlerRef;
74
75/// For [ValueType::Vector] result type
76#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
77pub struct PromSeriesVector {
78    pub metric: BTreeMap<String, String>,
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub value: Option<(f64, String)>,
81}
82
83/// For [ValueType::Matrix] result type
84#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
85pub struct PromSeriesMatrix {
86    pub metric: BTreeMap<String, String>,
87    pub values: Vec<(f64, String)>,
88}
89
90/// Variants corresponding to [ValueType]
91#[derive(Debug, Serialize, Deserialize, PartialEq)]
92#[serde(untagged)]
93pub enum PromQueryResult {
94    Matrix(Vec<PromSeriesMatrix>),
95    Vector(Vec<PromSeriesVector>),
96    Scalar(#[serde(skip_serializing_if = "Option::is_none")] Option<(f64, String)>),
97    String(#[serde(skip_serializing_if = "Option::is_none")] Option<(f64, String)>),
98}
99
100impl Default for PromQueryResult {
101    fn default() -> Self {
102        PromQueryResult::Matrix(Default::default())
103    }
104}
105
106#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
107pub struct PromData {
108    #[serde(rename = "resultType")]
109    pub result_type: String,
110    pub result: PromQueryResult,
111}
112
113/// A "holder" for the reference([Arc]) to a column name,
114/// to help avoiding cloning [String]s when used as a [HashMap] key.
115#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
116pub struct Column(Arc<String>);
117
118impl From<&str> for Column {
119    fn from(s: &str) -> Self {
120        Self(Arc::new(s.to_string()))
121    }
122}
123
124#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
125#[serde(untagged)]
126pub enum PrometheusResponse {
127    PromData(PromData),
128    Labels(Vec<String>),
129    Series(Vec<HashMap<Column, String>>),
130    LabelValues(Vec<String>),
131    FormatQuery(String),
132    BuildInfo(OwnedBuildInfo),
133    #[serde(skip_deserializing)]
134    ParseResult(promql_parser::parser::Expr),
135    #[default]
136    None,
137}
138
139impl PrometheusResponse {
140    /// Append the other [`PrometheusResponse]`.
141    /// # NOTE
142    ///   Only append matrix and vector results, otherwise just ignore the other response.
143    fn append(&mut self, other: PrometheusResponse) {
144        match (self, other) {
145            (
146                PrometheusResponse::PromData(PromData {
147                    result: PromQueryResult::Matrix(lhs),
148                    ..
149                }),
150                PrometheusResponse::PromData(PromData {
151                    result: PromQueryResult::Matrix(rhs),
152                    ..
153                }),
154            ) => {
155                lhs.extend(rhs);
156            }
157
158            (
159                PrometheusResponse::PromData(PromData {
160                    result: PromQueryResult::Vector(lhs),
161                    ..
162                }),
163                PrometheusResponse::PromData(PromData {
164                    result: PromQueryResult::Vector(rhs),
165                    ..
166                }),
167            ) => {
168                lhs.extend(rhs);
169            }
170            _ => {
171                // TODO(dennis): process other cases?
172            }
173        }
174    }
175
176    pub fn is_none(&self) -> bool {
177        matches!(self, PrometheusResponse::None)
178    }
179}
180
181#[derive(Debug, Default, Serialize, Deserialize)]
182pub struct FormatQuery {
183    query: Option<String>,
184}
185
186#[axum_macros::debug_handler]
187#[tracing::instrument(
188    skip_all,
189    fields(protocol = "prometheus", request_type = "format_query")
190)]
191pub async fn format_query(
192    State(_handler): State<PrometheusHandlerRef>,
193    Query(params): Query<InstantQuery>,
194    Extension(_query_ctx): Extension<QueryContext>,
195    Form(form_params): Form<InstantQuery>,
196) -> PrometheusJsonResponse {
197    let query = params.query.or(form_params.query).unwrap_or_default();
198    match promql_parser::parser::parse(&query) {
199        Ok(expr) => {
200            let pretty = expr.prettify();
201            PrometheusJsonResponse::success(PrometheusResponse::FormatQuery(pretty))
202        }
203        Err(reason) => {
204            let err = InvalidQuerySnafu { reason }.build();
205            PrometheusJsonResponse::error(err.status_code(), err.output_msg())
206        }
207    }
208}
209
210#[derive(Debug, Default, Serialize, Deserialize)]
211pub struct BuildInfoQuery {}
212
213#[axum_macros::debug_handler]
214#[tracing::instrument(
215    skip_all,
216    fields(protocol = "prometheus", request_type = "build_info_query")
217)]
218pub async fn build_info_query() -> PrometheusJsonResponse {
219    let build_info = common_version::build_info().clone();
220    PrometheusJsonResponse::success(PrometheusResponse::BuildInfo(build_info.into()))
221}
222
223#[derive(Debug, Default, Serialize, Deserialize)]
224pub struct InstantQuery {
225    query: Option<String>,
226    lookback: Option<String>,
227    time: Option<String>,
228    timeout: Option<String>,
229    db: Option<String>,
230}
231
232/// Helper macro which try to evaluate the expression and return its results.
233/// If the evaluation fails, return a `PrometheusJsonResponse` early.
234macro_rules! try_call_return_response {
235    ($handle: expr) => {
236        match $handle {
237            Ok(res) => res,
238            Err(err) => {
239                let msg = err.to_string();
240                return PrometheusJsonResponse::error(StatusCode::InvalidArguments, msg);
241            }
242        }
243    };
244}
245
246#[axum_macros::debug_handler]
247#[tracing::instrument(
248    skip_all,
249    fields(protocol = "prometheus", request_type = "instant_query")
250)]
251pub async fn instant_query(
252    State(handler): State<PrometheusHandlerRef>,
253    Query(params): Query<InstantQuery>,
254    Extension(mut query_ctx): Extension<QueryContext>,
255    Form(form_params): Form<InstantQuery>,
256) -> PrometheusJsonResponse {
257    // Extract time from query string, or use current server time if not specified.
258    let time = params
259        .time
260        .or(form_params.time)
261        .unwrap_or_else(current_time_rfc3339);
262    let prom_query = PromQuery {
263        query: params.query.or(form_params.query).unwrap_or_default(),
264        start: time.clone(),
265        end: time,
266        step: "1s".to_string(),
267        lookback: params
268            .lookback
269            .or(form_params.lookback)
270            .unwrap_or_else(|| DEFAULT_LOOKBACK_STRING.to_string()),
271        alias: None,
272    };
273
274    let promql_expr = try_call_return_response!(promql_parser::parser::parse(&prom_query.query));
275
276    // update catalog and schema in query context if necessary
277    if let Some(db) = &params.db {
278        let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
279        try_update_catalog_schema(&mut query_ctx, &catalog, &schema);
280    }
281    let query_ctx = Arc::new(query_ctx);
282
283    let _timer = crate::metrics::METRIC_HTTP_PROMETHEUS_PROMQL_ELAPSED
284        .with_label_values(&[query_ctx.get_db_string().as_str(), "instant_query"])
285        .start_timer();
286
287    if let Some(name_matchers) = find_metric_name_not_equal_matchers(&promql_expr)
288        && !name_matchers.is_empty()
289    {
290        debug!("Find metric name matchers: {:?}", name_matchers);
291
292        let metric_names =
293            try_call_return_response!(handler.query_metric_names(name_matchers, &query_ctx).await);
294
295        debug!("Find metric names: {:?}", metric_names);
296
297        if metric_names.is_empty() {
298            let result_type = promql_expr.value_type();
299
300            return PrometheusJsonResponse::success(PrometheusResponse::PromData(PromData {
301                result_type: result_type.to_string(),
302                ..Default::default()
303            }));
304        }
305
306        let responses = join_all(metric_names.into_iter().map(|metric| {
307            let mut prom_query = prom_query.clone();
308            let mut promql_expr = promql_expr.clone();
309            let query_ctx = query_ctx.clone();
310            let handler = handler.clone();
311
312            async move {
313                update_metric_name_matcher(&mut promql_expr, &metric);
314                let new_query = promql_expr.to_string();
315                debug!(
316                    "Updated promql, before: {}, after: {}",
317                    &prom_query.query, new_query
318                );
319                prom_query.query = new_query;
320
321                do_instant_query(&handler, &prom_query, query_ctx).await
322            }
323        }))
324        .await;
325
326        responses
327            .into_iter()
328            .reduce(|mut acc, resp| {
329                acc.data.append(resp.data);
330                acc
331            })
332            .unwrap()
333    } else {
334        do_instant_query(&handler, &prom_query, query_ctx).await
335    }
336}
337
338/// Executes a single instant query and returns response
339async fn do_instant_query(
340    handler: &PrometheusHandlerRef,
341    prom_query: &PromQuery,
342    query_ctx: QueryContextRef,
343) -> PrometheusJsonResponse {
344    let result = handler.do_query(prom_query, query_ctx).await;
345    let (metric_name, result_type) = match retrieve_metric_name_and_result_type(&prom_query.query) {
346        Ok((metric_name, result_type)) => (metric_name, result_type),
347        Err(err) => return PrometheusJsonResponse::error(err.status_code(), err.output_msg()),
348    };
349    PrometheusJsonResponse::from_query_result(result, metric_name, result_type).await
350}
351
352#[derive(Debug, Default, Serialize, Deserialize)]
353pub struct RangeQuery {
354    query: Option<String>,
355    start: Option<String>,
356    end: Option<String>,
357    step: Option<String>,
358    lookback: Option<String>,
359    timeout: Option<String>,
360    db: Option<String>,
361}
362
363#[axum_macros::debug_handler]
364#[tracing::instrument(
365    skip_all,
366    fields(protocol = "prometheus", request_type = "range_query")
367)]
368pub async fn range_query(
369    State(handler): State<PrometheusHandlerRef>,
370    Query(params): Query<RangeQuery>,
371    Extension(mut query_ctx): Extension<QueryContext>,
372    Form(form_params): Form<RangeQuery>,
373) -> PrometheusJsonResponse {
374    let prom_query = PromQuery {
375        query: params.query.or(form_params.query).unwrap_or_default(),
376        start: params.start.or(form_params.start).unwrap_or_default(),
377        end: params.end.or(form_params.end).unwrap_or_default(),
378        step: params.step.or(form_params.step).unwrap_or_default(),
379        lookback: params
380            .lookback
381            .or(form_params.lookback)
382            .unwrap_or_else(|| DEFAULT_LOOKBACK_STRING.to_string()),
383        alias: None,
384    };
385
386    let promql_expr = try_call_return_response!(promql_parser::parser::parse(&prom_query.query));
387
388    // update catalog and schema in query context if necessary
389    if let Some(db) = &params.db {
390        let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
391        try_update_catalog_schema(&mut query_ctx, &catalog, &schema);
392    }
393    let query_ctx = Arc::new(query_ctx);
394    let _timer = crate::metrics::METRIC_HTTP_PROMETHEUS_PROMQL_ELAPSED
395        .with_label_values(&[query_ctx.get_db_string().as_str(), "range_query"])
396        .start_timer();
397
398    if let Some(name_matchers) = find_metric_name_not_equal_matchers(&promql_expr)
399        && !name_matchers.is_empty()
400    {
401        debug!("Find metric name matchers: {:?}", name_matchers);
402
403        let metric_names =
404            try_call_return_response!(handler.query_metric_names(name_matchers, &query_ctx).await);
405
406        debug!("Find metric names: {:?}", metric_names);
407
408        if metric_names.is_empty() {
409            return PrometheusJsonResponse::success(PrometheusResponse::PromData(PromData {
410                result_type: ValueType::Matrix.to_string(),
411                ..Default::default()
412            }));
413        }
414
415        let responses = join_all(metric_names.into_iter().map(|metric| {
416            let mut prom_query = prom_query.clone();
417            let mut promql_expr = promql_expr.clone();
418            let query_ctx = query_ctx.clone();
419            let handler = handler.clone();
420
421            async move {
422                update_metric_name_matcher(&mut promql_expr, &metric);
423                let new_query = promql_expr.to_string();
424                debug!(
425                    "Updated promql, before: {}, after: {}",
426                    &prom_query.query, new_query
427                );
428                prom_query.query = new_query;
429
430                do_range_query(&handler, &prom_query, query_ctx).await
431            }
432        }))
433        .await;
434
435        // Safety: at least one responses, checked above
436        responses
437            .into_iter()
438            .reduce(|mut acc, resp| {
439                acc.data.append(resp.data);
440                acc
441            })
442            .unwrap()
443    } else {
444        do_range_query(&handler, &prom_query, query_ctx).await
445    }
446}
447
448/// Executes a single range query and returns response
449async fn do_range_query(
450    handler: &PrometheusHandlerRef,
451    prom_query: &PromQuery,
452    query_ctx: QueryContextRef,
453) -> PrometheusJsonResponse {
454    let result = handler.do_query(prom_query, query_ctx).await;
455    let metric_name = match retrieve_metric_name_and_result_type(&prom_query.query) {
456        Err(err) => return PrometheusJsonResponse::error(err.status_code(), err.output_msg()),
457        Ok((metric_name, _)) => metric_name,
458    };
459    PrometheusJsonResponse::from_query_result(result, metric_name, ValueType::Matrix).await
460}
461
462#[derive(Debug, Default, Serialize)]
463struct Matches(Vec<String>);
464
465#[derive(Debug, Default, Serialize, Deserialize)]
466pub struct LabelsQuery {
467    start: Option<String>,
468    end: Option<String>,
469    lookback: Option<String>,
470    #[serde(flatten)]
471    matches: Matches,
472    db: Option<String>,
473}
474
475// Custom Deserialize method to support parsing repeated match[]
476impl<'de> Deserialize<'de> for Matches {
477    fn deserialize<D>(deserializer: D) -> std::result::Result<Matches, D::Error>
478    where
479        D: de::Deserializer<'de>,
480    {
481        struct MatchesVisitor;
482
483        impl<'d> Visitor<'d> for MatchesVisitor {
484            type Value = Vec<String>;
485
486            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
487                formatter.write_str("a string")
488            }
489
490            fn visit_map<M>(self, mut access: M) -> std::result::Result<Self::Value, M::Error>
491            where
492                M: MapAccess<'d>,
493            {
494                let mut matches = Vec::new();
495                while let Some((key, value)) = access.next_entry::<String, String>()? {
496                    if key == "match[]" {
497                        matches.push(value);
498                    }
499                }
500                Ok(matches)
501            }
502        }
503        Ok(Matches(deserializer.deserialize_map(MatchesVisitor)?))
504    }
505}
506
507/// Handles schema errors, transforming a Result into an Option.
508/// - If the input is `Ok(v)`, returns `Some(v)`
509/// - If the input is `Err(err)` and the error status code is `TableNotFound` or
510///   `TableColumnNotFound`, returns `None` (ignoring these specific errors)
511/// - If the input is `Err(err)` with any other error code, directly returns a
512///   `PrometheusJsonResponse::error`.
513macro_rules! handle_schema_err {
514    ($result:expr) => {
515        match $result {
516            Ok(v) => Some(v),
517            Err(err) => {
518                if err.status_code() == StatusCode::TableNotFound
519                    || err.status_code() == StatusCode::TableColumnNotFound
520                {
521                    // Prometheus won't report error if querying nonexist label and metric
522                    None
523                } else {
524                    return PrometheusJsonResponse::error(err.status_code(), err.output_msg());
525                }
526            }
527        }
528    };
529}
530
531#[axum_macros::debug_handler]
532#[tracing::instrument(
533    skip_all,
534    fields(protocol = "prometheus", request_type = "labels_query")
535)]
536pub async fn labels_query(
537    State(handler): State<PrometheusHandlerRef>,
538    Query(params): Query<LabelsQuery>,
539    Extension(mut query_ctx): Extension<QueryContext>,
540    Form(form_params): Form<LabelsQuery>,
541) -> PrometheusJsonResponse {
542    let (catalog, schema) = get_catalog_schema(&params.db, &query_ctx);
543    try_update_catalog_schema(&mut query_ctx, &catalog, &schema);
544    let query_ctx = Arc::new(query_ctx);
545
546    let mut queries = params.matches.0;
547    if queries.is_empty() {
548        queries = form_params.matches.0;
549    }
550
551    let _timer = crate::metrics::METRIC_HTTP_PROMETHEUS_PROMQL_ELAPSED
552        .with_label_values(&[query_ctx.get_db_string().as_str(), "labels_query"])
553        .start_timer();
554
555    // Fetch all tag columns. It will be used as white-list for tag names.
556    let mut labels = match get_all_column_names(&catalog, &schema, &handler.catalog_manager()).await
557    {
558        Ok(labels) => labels,
559        Err(e) => return PrometheusJsonResponse::error(e.status_code(), e.output_msg()),
560    };
561    // insert the special metric name label
562    let _ = labels.insert(METRIC_NAME.to_string());
563
564    // Fetch all columns if no query matcher is provided
565    if queries.is_empty() {
566        let mut labels_vec = labels.into_iter().collect::<Vec<_>>();
567        labels_vec.sort_unstable();
568        return PrometheusJsonResponse::success(PrometheusResponse::Labels(labels_vec));
569    }
570
571    // Otherwise, run queries and extract column name from result set.
572    let start = params
573        .start
574        .or(form_params.start)
575        .unwrap_or_else(yesterday_rfc3339);
576    let end = params
577        .end
578        .or(form_params.end)
579        .unwrap_or_else(current_time_rfc3339);
580    let lookback = params
581        .lookback
582        .or(form_params.lookback)
583        .unwrap_or_else(|| DEFAULT_LOOKBACK_STRING.to_string());
584
585    let mut fetched_labels = HashSet::new();
586    let _ = fetched_labels.insert(METRIC_NAME.to_string());
587
588    let mut merge_map = HashMap::new();
589    for query in queries {
590        let prom_query = PromQuery {
591            query,
592            start: start.clone(),
593            end: end.clone(),
594            step: DEFAULT_LOOKBACK_STRING.to_string(),
595            lookback: lookback.clone(),
596            alias: None,
597        };
598
599        let result = handler.do_query(&prom_query, query_ctx.clone()).await;
600        handle_schema_err!(
601            retrieve_labels_name_from_query_result(result, &mut fetched_labels, &mut merge_map)
602                .await
603        );
604    }
605
606    // intersect `fetched_labels` with `labels` to filter out non-tag columns
607    fetched_labels.retain(|l| labels.contains(l));
608    let _ = labels.insert(METRIC_NAME.to_string());
609
610    let mut sorted_labels: Vec<String> = fetched_labels.into_iter().collect();
611    sorted_labels.sort();
612    let merge_map = merge_map
613        .into_iter()
614        .map(|(k, v)| (k, Value::from(v)))
615        .collect();
616    let mut resp = PrometheusJsonResponse::success(PrometheusResponse::Labels(sorted_labels));
617    resp.resp_metrics = merge_map;
618    resp
619}
620
621/// Get all tag column name of the given schema
622async fn get_all_column_names(
623    catalog: &str,
624    schema: &str,
625    manager: &CatalogManagerRef,
626) -> std::result::Result<HashSet<String>, catalog::error::Error> {
627    let mut labels = HashSet::new();
628    let mut tables = manager.tables(catalog, schema, None);
629    while let Some(table) = tables.try_next().await? {
630        for column in table.primary_key_columns() {
631            if column.name != DATA_SCHEMA_TABLE_ID_COLUMN_NAME
632                && column.name != DATA_SCHEMA_TSID_COLUMN_NAME
633            {
634                labels.insert(column.name);
635            }
636        }
637    }
638
639    Ok(labels)
640}
641
642async fn retrieve_series_from_query_result(
643    result: Result<Output>,
644    series: &mut Vec<HashMap<Column, String>>,
645    query_ctx: &QueryContext,
646    table_name: &str,
647    manager: &CatalogManagerRef,
648    metrics: &mut HashMap<String, u64>,
649) -> Result<()> {
650    let result = result?;
651
652    // fetch tag list
653    let table = manager
654        .table(
655            query_ctx.current_catalog(),
656            &query_ctx.current_schema(),
657            table_name,
658            Some(query_ctx),
659        )
660        .await?
661        .with_context(|| TableNotFoundSnafu {
662            catalog: query_ctx.current_catalog(),
663            schema: query_ctx.current_schema(),
664            table: table_name,
665        })?;
666    let tag_columns = table
667        .primary_key_columns()
668        .map(|c| c.name)
669        .collect::<HashSet<_>>();
670
671    match result.data {
672        OutputData::RecordBatches(batches) => {
673            record_batches_to_series(batches, series, table_name, &tag_columns)
674        }
675        OutputData::Stream(stream) => {
676            let batches = RecordBatches::try_collect(stream)
677                .await
678                .context(CollectRecordbatchSnafu)?;
679            record_batches_to_series(batches, series, table_name, &tag_columns)
680        }
681        OutputData::AffectedRows(_) => Err(Error::UnexpectedResult {
682            reason: "expected data result, but got affected rows".to_string(),
683            location: Location::default(),
684        }),
685    }?;
686
687    if let Some(ref plan) = result.meta.plan {
688        collect_plan_metrics(plan, &mut [metrics]);
689    }
690    Ok(())
691}
692
693/// Retrieve labels name from query result
694async fn retrieve_labels_name_from_query_result(
695    result: Result<Output>,
696    labels: &mut HashSet<String>,
697    metrics: &mut HashMap<String, u64>,
698) -> Result<()> {
699    let result = result?;
700    match result.data {
701        OutputData::RecordBatches(batches) => record_batches_to_labels_name(batches, labels),
702        OutputData::Stream(stream) => {
703            let batches = RecordBatches::try_collect(stream)
704                .await
705                .context(CollectRecordbatchSnafu)?;
706            record_batches_to_labels_name(batches, labels)
707        }
708        OutputData::AffectedRows(_) => UnexpectedResultSnafu {
709            reason: "expected data result, but got affected rows".to_string(),
710        }
711        .fail(),
712    }?;
713    if let Some(ref plan) = result.meta.plan {
714        collect_plan_metrics(plan, &mut [metrics]);
715    }
716    Ok(())
717}
718
719fn record_batches_to_series(
720    batches: RecordBatches,
721    series: &mut Vec<HashMap<Column, String>>,
722    table_name: &str,
723    tag_columns: &HashSet<String>,
724) -> Result<()> {
725    for batch in batches.iter() {
726        // project record batch to only contains tag columns
727        let projection = batch
728            .schema
729            .column_schemas()
730            .iter()
731            .enumerate()
732            .filter_map(|(idx, col)| {
733                if tag_columns.contains(&col.name) {
734                    Some(idx)
735                } else {
736                    None
737                }
738            })
739            .collect::<Vec<_>>();
740        let batch = batch
741            .try_project(&projection)
742            .context(CollectRecordbatchSnafu)?;
743
744        let mut writer = RowWriter::new(&batch.schema, table_name);
745        writer.write(batch, series)?;
746    }
747    Ok(())
748}
749
750/// Writer from a row in the record batch to a Prometheus time series:
751///
752/// `{__name__="<metric name>", <label name>="<label value>", ...}`
753///
754/// The metrics name is the table name; label names are the column names and
755/// the label values are the corresponding row values (all are converted to strings).
756struct RowWriter {
757    /// The template that is to produce a Prometheus time series. It is pre-filled with metrics name
758    /// and label names, waiting to be filled by row values afterward.
759    template: HashMap<Column, Option<String>>,
760    /// The current filling row.
761    current: Option<HashMap<Column, Option<String>>>,
762}
763
764impl RowWriter {
765    fn new(schema: &SchemaRef, table: &str) -> Self {
766        let mut template = schema
767            .column_schemas()
768            .iter()
769            .map(|x| (x.name.as_str().into(), None))
770            .collect::<HashMap<Column, Option<String>>>();
771        template.insert("__name__".into(), Some(table.to_string()));
772        Self {
773            template,
774            current: None,
775        }
776    }
777
778    fn insert(&mut self, column: ColumnRef, value: impl ToString) {
779        let current = self.current.get_or_insert_with(|| self.template.clone());
780        match current.get_mut(&column as &dyn AsColumnRef) {
781            Some(x) => {
782                let _ = x.insert(value.to_string());
783            }
784            None => {
785                let _ = current.insert(column.0.into(), Some(value.to_string()));
786            }
787        }
788    }
789
790    fn insert_bytes(&mut self, column_schema: &ColumnSchema, bytes: &[u8]) -> Result<()> {
791        let column_name = column_schema.name.as_str().into();
792
793        if column_schema.data_type.is_json() {
794            let s = jsonb_to_string(bytes).context(ConvertScalarValueSnafu)?;
795            self.insert(column_name, s);
796        } else {
797            let hex = bytes
798                .iter()
799                .map(|b| format!("{b:02x}"))
800                .collect::<Vec<String>>()
801                .join("");
802            self.insert(column_name, hex);
803        }
804        Ok(())
805    }
806
807    fn finish(&mut self) -> HashMap<Column, String> {
808        let Some(current) = self.current.take() else {
809            return HashMap::new();
810        };
811        current
812            .into_iter()
813            .filter_map(|(k, v)| v.map(|v| (k, v)))
814            .collect()
815    }
816
817    fn write(
818        &mut self,
819        record_batch: RecordBatch,
820        series: &mut Vec<HashMap<Column, String>>,
821    ) -> Result<()> {
822        let schema = record_batch.schema.clone();
823        let record_batch = record_batch.into_df_record_batch();
824        for i in 0..record_batch.num_rows() {
825            for (j, array) in record_batch.columns().iter().enumerate() {
826                let column = schema.column_name_by_index(j).into();
827
828                if array.is_null(i) {
829                    self.insert(column, "Null");
830                    continue;
831                }
832
833                match array.data_type() {
834                    DataType::Null => {
835                        self.insert(column, "Null");
836                    }
837                    DataType::Boolean => {
838                        let array = array.as_boolean();
839                        let v = array.value(i);
840                        self.insert(column, v);
841                    }
842                    DataType::UInt8 => {
843                        let array = array.as_primitive::<UInt8Type>();
844                        let v = array.value(i);
845                        self.insert(column, v);
846                    }
847                    DataType::UInt16 => {
848                        let array = array.as_primitive::<UInt16Type>();
849                        let v = array.value(i);
850                        self.insert(column, v);
851                    }
852                    DataType::UInt32 => {
853                        let array = array.as_primitive::<UInt32Type>();
854                        let v = array.value(i);
855                        self.insert(column, v);
856                    }
857                    DataType::UInt64 => {
858                        let array = array.as_primitive::<UInt64Type>();
859                        let v = array.value(i);
860                        self.insert(column, v);
861                    }
862                    DataType::Int8 => {
863                        let array = array.as_primitive::<Int8Type>();
864                        let v = array.value(i);
865                        self.insert(column, v);
866                    }
867                    DataType::Int16 => {
868                        let array = array.as_primitive::<Int16Type>();
869                        let v = array.value(i);
870                        self.insert(column, v);
871                    }
872                    DataType::Int32 => {
873                        let array = array.as_primitive::<Int32Type>();
874                        let v = array.value(i);
875                        self.insert(column, v);
876                    }
877                    DataType::Int64 => {
878                        let array = array.as_primitive::<Int64Type>();
879                        let v = array.value(i);
880                        self.insert(column, v);
881                    }
882                    DataType::Float32 => {
883                        let array = array.as_primitive::<Float32Type>();
884                        let v = array.value(i);
885                        self.insert(column, v);
886                    }
887                    DataType::Float64 => {
888                        let array = array.as_primitive::<Float64Type>();
889                        let v = array.value(i);
890                        self.insert(column, v);
891                    }
892                    DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View => {
893                        let v = datatypes::arrow_array::string_array_value(array, i);
894                        self.insert(column, v);
895                    }
896                    DataType::Binary | DataType::LargeBinary | DataType::BinaryView => {
897                        let v = datatypes::arrow_array::binary_array_value(array, i);
898                        let column_schema = &schema.column_schemas()[j];
899                        self.insert_bytes(column_schema, v)?;
900                    }
901                    DataType::Date32 => {
902                        let array = array.as_primitive::<Date32Type>();
903                        let v = Date::new(array.value(i));
904                        self.insert(column, v);
905                    }
906                    DataType::Date64 => {
907                        let array = array.as_primitive::<Date64Type>();
908                        // `Date64` values are milliseconds representation of `Date32` values,
909                        // according to its specification. So we convert the `Date64` value here to
910                        // the `Date32` value to process them unified.
911                        let v = Date::new((array.value(i) / 86_400_000) as i32);
912                        self.insert(column, v);
913                    }
914                    DataType::Timestamp(_, _) => {
915                        let v = datatypes::arrow_array::timestamp_array_value(array, i);
916                        self.insert(column, v.to_iso8601_string());
917                    }
918                    DataType::Time32(_) | DataType::Time64(_) => {
919                        let v = datatypes::arrow_array::time_array_value(array, i);
920                        self.insert(column, v.to_iso8601_string());
921                    }
922                    DataType::Interval(interval_unit) => match interval_unit {
923                        IntervalUnit::YearMonth => {
924                            let array = array.as_primitive::<IntervalYearMonthType>();
925                            let v: IntervalYearMonth = array.value(i).into();
926                            self.insert(column, v.to_iso8601_string());
927                        }
928                        IntervalUnit::DayTime => {
929                            let array = array.as_primitive::<IntervalDayTimeType>();
930                            let v: IntervalDayTime = array.value(i).into();
931                            self.insert(column, v.to_iso8601_string());
932                        }
933                        IntervalUnit::MonthDayNano => {
934                            let array = array.as_primitive::<IntervalMonthDayNanoType>();
935                            let v: IntervalMonthDayNano = array.value(i).into();
936                            self.insert(column, v.to_iso8601_string());
937                        }
938                    },
939                    DataType::Duration(_) => {
940                        let d = datatypes::arrow_array::duration_array_value(array, i);
941                        self.insert(column, d);
942                    }
943                    DataType::List(_) => {
944                        let v = ScalarValue::try_from_array(array, i).context(DataFusionSnafu)?;
945                        self.insert(column, v);
946                    }
947                    DataType::Struct(_) => {
948                        let v = ScalarValue::try_from_array(array, i).context(DataFusionSnafu)?;
949                        self.insert(column, v);
950                    }
951                    DataType::Decimal128(precision, scale) => {
952                        let array = array.as_primitive::<Decimal128Type>();
953                        let v = Decimal128::new(array.value(i), *precision, *scale);
954                        self.insert(column, v);
955                    }
956                    _ => {
957                        return NotSupportedSnafu {
958                            feat: format!("convert {} to http value", array.data_type()),
959                        }
960                        .fail();
961                    }
962                }
963            }
964
965            series.push(self.finish())
966        }
967        Ok(())
968    }
969}
970
971#[derive(Debug, Clone, Copy, PartialEq)]
972struct ColumnRef<'a>(&'a str);
973
974impl<'a> From<&'a str> for ColumnRef<'a> {
975    fn from(s: &'a str) -> Self {
976        Self(s)
977    }
978}
979
980trait AsColumnRef {
981    fn as_ref(&self) -> ColumnRef<'_>;
982}
983
984impl AsColumnRef for Column {
985    fn as_ref(&self) -> ColumnRef<'_> {
986        self.0.as_str().into()
987    }
988}
989
990impl AsColumnRef for ColumnRef<'_> {
991    fn as_ref(&self) -> ColumnRef<'_> {
992        *self
993    }
994}
995
996impl<'a> PartialEq for dyn AsColumnRef + 'a {
997    fn eq(&self, other: &Self) -> bool {
998        self.as_ref() == other.as_ref()
999    }
1000}
1001
1002impl<'a> Eq for dyn AsColumnRef + 'a {}
1003
1004impl<'a> Hash for dyn AsColumnRef + 'a {
1005    fn hash<H: Hasher>(&self, state: &mut H) {
1006        self.as_ref().0.hash(state);
1007    }
1008}
1009
1010impl<'a> Borrow<dyn AsColumnRef + 'a> for Column {
1011    fn borrow(&self) -> &(dyn AsColumnRef + 'a) {
1012        self
1013    }
1014}
1015
1016/// Retrieve labels name from record batches
1017fn record_batches_to_labels_name(
1018    batches: RecordBatches,
1019    labels: &mut HashSet<String>,
1020) -> Result<()> {
1021    let mut column_indices = Vec::new();
1022    let mut field_column_indices = Vec::new();
1023    for (i, column) in batches.schema().column_schemas().iter().enumerate() {
1024        if let ConcreteDataType::Float64(_) = column.data_type {
1025            field_column_indices.push(i);
1026        }
1027        column_indices.push(i);
1028    }
1029
1030    if field_column_indices.is_empty() {
1031        return Err(Error::Internal {
1032            err_msg: "no value column found".to_string(),
1033        });
1034    }
1035
1036    for batch in batches.iter() {
1037        let names = column_indices
1038            .iter()
1039            .map(|c| batches.schema().column_name_by_index(*c).to_string())
1040            .collect::<Vec<_>>();
1041
1042        let field_columns = field_column_indices
1043            .iter()
1044            .map(|i| {
1045                let column = batch.column(*i);
1046                column.as_primitive::<Float64Type>()
1047            })
1048            .collect::<Vec<_>>();
1049
1050        for row_index in 0..batch.num_rows() {
1051            // if all field columns are null, skip this row
1052            if field_columns.iter().all(|c| c.is_null(row_index)) {
1053                continue;
1054            }
1055
1056            // if a field is not null, record the tag name and return
1057            names.iter().for_each(|name| {
1058                let _ = labels.insert(name.clone());
1059            });
1060            return Ok(());
1061        }
1062    }
1063    Ok(())
1064}
1065
1066pub(crate) fn retrieve_metric_name_and_result_type(
1067    promql: &str,
1068) -> Result<(Option<String>, ValueType)> {
1069    let promql_expr = promql_parser::parser::parse(promql)
1070        .map_err(|reason| InvalidQuerySnafu { reason }.build())?;
1071    let metric_name = promql_expr_to_metric_name(&promql_expr);
1072    let result_type = promql_expr.value_type();
1073
1074    Ok((metric_name, result_type))
1075}
1076
1077/// Tries to get catalog and schema from an optional db param. And retrieves
1078/// them from [QueryContext] if they don't present.
1079pub(crate) fn get_catalog_schema(db: &Option<String>, ctx: &QueryContext) -> (String, String) {
1080    if let Some(db) = db {
1081        parse_catalog_and_schema_from_db_string(db)
1082    } else {
1083        (
1084            ctx.current_catalog().to_string(),
1085            ctx.current_schema().clone(),
1086        )
1087    }
1088}
1089
1090/// Update catalog and schema in [QueryContext] if necessary.
1091pub(crate) fn try_update_catalog_schema(ctx: &mut QueryContext, catalog: &str, schema: &str) {
1092    if ctx.current_catalog() != catalog || ctx.current_schema() != schema {
1093        ctx.set_current_catalog(catalog);
1094        ctx.set_current_schema(schema);
1095    }
1096}
1097
1098fn promql_expr_to_metric_name(expr: &PromqlExpr) -> Option<String> {
1099    let mut metric_names = HashSet::new();
1100    collect_metric_names(expr, &mut metric_names);
1101
1102    // Return the metric name only if there's exactly one unique metric name
1103    if metric_names.len() == 1 {
1104        metric_names.into_iter().next()
1105    } else {
1106        None
1107    }
1108}
1109
1110/// Recursively collect all metric names from a PromQL expression
1111fn collect_metric_names(expr: &PromqlExpr, metric_names: &mut HashSet<String>) {
1112    match expr {
1113        PromqlExpr::Aggregate(AggregateExpr { modifier, expr, .. }) => {
1114            match modifier {
1115                Some(LabelModifier::Include(labels))
1116                    if !labels.labels.contains(&METRIC_NAME.to_string()) =>
1117                {
1118                    metric_names.clear();
1119                    return;
1120                }
1121                Some(LabelModifier::Exclude(labels))
1122                    if labels.labels.contains(&METRIC_NAME.to_string()) =>
1123                {
1124                    metric_names.clear();
1125                    return;
1126                }
1127                _ => {}
1128            }
1129            collect_metric_names(expr, metric_names)
1130        }
1131        PromqlExpr::Unary(UnaryExpr { .. }) => metric_names.clear(),
1132        PromqlExpr::Binary(BinaryExpr { lhs, op, .. }) => {
1133            if matches!(
1134                op.id(),
1135                token::T_LAND // INTERSECT
1136                    | token::T_LOR // UNION
1137                    | token::T_LUNLESS // EXCEPT
1138            ) {
1139                collect_metric_names(lhs, metric_names)
1140            } else {
1141                metric_names.clear()
1142            }
1143        }
1144        PromqlExpr::Paren(ParenExpr { expr }) => collect_metric_names(expr, metric_names),
1145        PromqlExpr::Subquery(SubqueryExpr { expr, .. }) => collect_metric_names(expr, metric_names),
1146        PromqlExpr::VectorSelector(VectorSelector { name, matchers, .. }) => {
1147            if let Some(name) = name {
1148                metric_names.insert(name.clone());
1149            } else if let Some(matcher) = matchers.find_matchers(METRIC_NAME).into_iter().next() {
1150                metric_names.insert(matcher.value);
1151            }
1152        }
1153        PromqlExpr::MatrixSelector(MatrixSelector { vs, .. }) => {
1154            let VectorSelector { name, matchers, .. } = vs;
1155            if let Some(name) = name {
1156                metric_names.insert(name.clone());
1157            } else if let Some(matcher) = matchers.find_matchers(METRIC_NAME).into_iter().next() {
1158                metric_names.insert(matcher.value);
1159            }
1160        }
1161        PromqlExpr::Call(Call { args, .. }) => {
1162            args.args
1163                .iter()
1164                .for_each(|e| collect_metric_names(e, metric_names));
1165        }
1166        PromqlExpr::NumberLiteral(_) | PromqlExpr::StringLiteral(_) | PromqlExpr::Extension(_) => {}
1167    }
1168}
1169
1170fn find_metric_name_and_matchers<E, F>(expr: &PromqlExpr, f: F) -> Option<E>
1171where
1172    F: Fn(&Option<String>, &Matchers) -> Option<E> + Clone,
1173{
1174    match expr {
1175        PromqlExpr::Aggregate(AggregateExpr { expr, .. }) => find_metric_name_and_matchers(expr, f),
1176        PromqlExpr::Unary(UnaryExpr { expr }) => find_metric_name_and_matchers(expr, f),
1177        PromqlExpr::Binary(BinaryExpr { lhs, rhs, .. }) => {
1178            find_metric_name_and_matchers(lhs, f.clone()).or(find_metric_name_and_matchers(rhs, f))
1179        }
1180        PromqlExpr::Paren(ParenExpr { expr }) => find_metric_name_and_matchers(expr, f),
1181        PromqlExpr::Subquery(SubqueryExpr { expr, .. }) => find_metric_name_and_matchers(expr, f),
1182        PromqlExpr::NumberLiteral(_) => None,
1183        PromqlExpr::StringLiteral(_) => None,
1184        PromqlExpr::Extension(_) => None,
1185        PromqlExpr::VectorSelector(VectorSelector { name, matchers, .. }) => f(name, matchers),
1186        PromqlExpr::MatrixSelector(MatrixSelector { vs, .. }) => {
1187            let VectorSelector { name, matchers, .. } = vs;
1188
1189            f(name, matchers)
1190        }
1191        PromqlExpr::Call(Call { args, .. }) => args
1192            .args
1193            .iter()
1194            .find_map(|e| find_metric_name_and_matchers(e, f.clone())),
1195    }
1196}
1197
1198/// Try to find the `__name__` matchers which op is not `MatchOp::Equal`.
1199fn find_metric_name_not_equal_matchers(expr: &PromqlExpr) -> Option<Vec<Matcher>> {
1200    find_metric_name_and_matchers(expr, |name, matchers| {
1201        // Has name, ignore the matchers
1202        if name.is_some() {
1203            return None;
1204        }
1205
1206        // FIXME(dennis): we don't consider the nested and `or` matchers yet.
1207        Some(matchers.find_matchers(METRIC_NAME))
1208    })
1209    .map(|matchers| {
1210        matchers
1211            .into_iter()
1212            .filter(|m| !matches!(m.op, MatchOp::Equal))
1213            .collect::<Vec<_>>()
1214    })
1215}
1216
1217/// Update the `__name__` matchers in expression into special value
1218/// Returns the updated expression.
1219fn update_metric_name_matcher(expr: &mut PromqlExpr, metric_name: &str) {
1220    match expr {
1221        PromqlExpr::Aggregate(AggregateExpr { expr, .. }) => {
1222            update_metric_name_matcher(expr, metric_name)
1223        }
1224        PromqlExpr::Unary(UnaryExpr { expr }) => update_metric_name_matcher(expr, metric_name),
1225        PromqlExpr::Binary(BinaryExpr { lhs, rhs, .. }) => {
1226            update_metric_name_matcher(lhs, metric_name);
1227            update_metric_name_matcher(rhs, metric_name);
1228        }
1229        PromqlExpr::Paren(ParenExpr { expr }) => update_metric_name_matcher(expr, metric_name),
1230        PromqlExpr::Subquery(SubqueryExpr { expr, .. }) => {
1231            update_metric_name_matcher(expr, metric_name)
1232        }
1233        PromqlExpr::VectorSelector(VectorSelector { name, matchers, .. }) => {
1234            if name.is_some() {
1235                return;
1236            }
1237
1238            for m in &mut matchers.matchers {
1239                if m.name == METRIC_NAME {
1240                    m.op = MatchOp::Equal;
1241                    m.value = metric_name.to_string();
1242                }
1243            }
1244        }
1245        PromqlExpr::MatrixSelector(MatrixSelector { vs, .. }) => {
1246            let VectorSelector { name, matchers, .. } = vs;
1247            if name.is_some() {
1248                return;
1249            }
1250
1251            for m in &mut matchers.matchers {
1252                if m.name == METRIC_NAME {
1253                    m.op = MatchOp::Equal;
1254                    m.value = metric_name.to_string();
1255                }
1256            }
1257        }
1258        PromqlExpr::Call(Call { args, .. }) => {
1259            args.args.iter_mut().for_each(|e| {
1260                update_metric_name_matcher(e, metric_name);
1261            });
1262        }
1263        PromqlExpr::NumberLiteral(_) | PromqlExpr::StringLiteral(_) | PromqlExpr::Extension(_) => {}
1264    }
1265}
1266
1267#[derive(Debug, Default, Serialize, Deserialize)]
1268pub struct LabelValueQuery {
1269    start: Option<String>,
1270    end: Option<String>,
1271    lookback: Option<String>,
1272    #[serde(flatten)]
1273    matches: Matches,
1274    db: Option<String>,
1275    limit: Option<usize>,
1276}
1277
1278#[axum_macros::debug_handler]
1279#[tracing::instrument(
1280    skip_all,
1281    fields(protocol = "prometheus", request_type = "label_values_query")
1282)]
1283pub async fn label_values_query(
1284    State(handler): State<PrometheusHandlerRef>,
1285    Path(label_name): Path<String>,
1286    Extension(mut query_ctx): Extension<QueryContext>,
1287    Query(params): Query<LabelValueQuery>,
1288) -> PrometheusJsonResponse {
1289    let (catalog, schema) = get_catalog_schema(&params.db, &query_ctx);
1290    try_update_catalog_schema(&mut query_ctx, &catalog, &schema);
1291    let query_ctx = Arc::new(query_ctx);
1292
1293    let _timer = crate::metrics::METRIC_HTTP_PROMETHEUS_PROMQL_ELAPSED
1294        .with_label_values(&[query_ctx.get_db_string().as_str(), "label_values_query"])
1295        .start_timer();
1296
1297    if label_name == METRIC_NAME_LABEL {
1298        let catalog_manager = handler.catalog_manager();
1299
1300        let mut table_names = try_call_return_response!(
1301            retrieve_table_names(&query_ctx, catalog_manager, params.matches.0).await
1302        );
1303
1304        truncate_results(&mut table_names, params.limit);
1305        return PrometheusJsonResponse::success(PrometheusResponse::LabelValues(table_names));
1306    } else if label_name == FIELD_NAME_LABEL {
1307        let field_columns = handle_schema_err!(
1308            retrieve_field_names(&query_ctx, handler.catalog_manager(), params.matches.0).await
1309        )
1310        .unwrap_or_default();
1311        let mut field_columns = field_columns.into_iter().collect::<Vec<_>>();
1312        field_columns.sort_unstable();
1313        truncate_results(&mut field_columns, params.limit);
1314        return PrometheusJsonResponse::success(PrometheusResponse::LabelValues(field_columns));
1315    } else if is_database_selection_label(&label_name) {
1316        let catalog_manager = handler.catalog_manager();
1317
1318        let mut schema_names = try_call_return_response!(
1319            retrieve_schema_names(&query_ctx, catalog_manager, params.matches.0).await
1320        );
1321        truncate_results(&mut schema_names, params.limit);
1322        return PrometheusJsonResponse::success(PrometheusResponse::LabelValues(schema_names));
1323    }
1324
1325    let queries = params.matches.0;
1326    if queries.is_empty() {
1327        return PrometheusJsonResponse::error(
1328            StatusCode::InvalidArguments,
1329            "match[] parameter is required",
1330        );
1331    }
1332
1333    let start = params.start.unwrap_or_else(yesterday_rfc3339);
1334    let end = params.end.unwrap_or_else(current_time_rfc3339);
1335    let mut label_values = HashSet::new();
1336
1337    let start = try_call_return_response!(
1338        QueryLanguageParser::parse_promql_timestamp(&start)
1339            .context(ParseTimestampSnafu { timestamp: &start })
1340    );
1341    let end = try_call_return_response!(
1342        QueryLanguageParser::parse_promql_timestamp(&end)
1343            .context(ParseTimestampSnafu { timestamp: &end })
1344    );
1345
1346    for query in queries {
1347        let promql_expr = try_call_return_response!(promql_parser::parser::parse(&query));
1348        let PromqlExpr::VectorSelector(mut vector_selector) = promql_expr else {
1349            return PrometheusJsonResponse::error(
1350                StatusCode::InvalidArguments,
1351                "expected vector selector",
1352            );
1353        };
1354        let Some(name) = take_metric_name(&mut vector_selector) else {
1355            return PrometheusJsonResponse::error(
1356                StatusCode::InvalidArguments,
1357                "expected metric name",
1358            );
1359        };
1360        let VectorSelector { matchers, .. } = vector_selector;
1361        // Only use and filter matchers.
1362        let matchers = matchers.matchers;
1363        let result = handler
1364            .query_label_values(name, label_name.clone(), matchers, start, end, &query_ctx)
1365            .await;
1366        if let Some(result) = handle_schema_err!(result) {
1367            label_values.extend(result.into_iter());
1368        }
1369    }
1370
1371    let mut label_values: Vec<_> = label_values.into_iter().collect();
1372    label_values.sort_unstable();
1373    truncate_results(&mut label_values, params.limit);
1374
1375    PrometheusJsonResponse::success(PrometheusResponse::LabelValues(label_values))
1376}
1377
1378fn truncate_results(label_values: &mut Vec<String>, limit: Option<usize>) {
1379    if let Some(limit) = limit
1380        && limit > 0
1381        && label_values.len() >= limit
1382    {
1383        label_values.truncate(limit);
1384    }
1385}
1386
1387/// Take metric name from the [VectorSelector].
1388/// It takes the name in the selector or removes the name matcher.
1389fn take_metric_name(selector: &mut VectorSelector) -> Option<String> {
1390    if let Some(name) = selector.name.take() {
1391        return Some(name);
1392    }
1393
1394    let (pos, matcher) = selector
1395        .matchers
1396        .matchers
1397        .iter()
1398        .find_position(|matcher| matcher.name == "__name__" && matcher.op == MatchOp::Equal)?;
1399    let name = matcher.value.clone();
1400    // We need to remove the name matcher to avoid using it as a filter in query.
1401    selector.matchers.matchers.remove(pos);
1402
1403    Some(name)
1404}
1405
1406async fn retrieve_table_names(
1407    query_ctx: &QueryContext,
1408    catalog_manager: CatalogManagerRef,
1409    matches: Vec<String>,
1410) -> Result<Vec<String>> {
1411    let catalog = query_ctx.current_catalog();
1412    let schema = query_ctx.current_schema();
1413
1414    let mut tables_stream = catalog_manager.tables(catalog, &schema, Some(query_ctx));
1415    let mut table_names = Vec::new();
1416
1417    // we only provide very limited support for matcher against __name__
1418    let name_matcher = matches
1419        .first()
1420        .and_then(|matcher| promql_parser::parser::parse(matcher).ok())
1421        .and_then(|expr| {
1422            if let PromqlExpr::VectorSelector(vector_selector) = expr {
1423                let matchers = vector_selector.matchers.matchers;
1424                for matcher in matchers {
1425                    if matcher.name == METRIC_NAME_LABEL {
1426                        return Some(matcher);
1427                    }
1428                }
1429
1430                None
1431            } else {
1432                None
1433            }
1434        });
1435
1436    while let Some(table) = tables_stream.next().await {
1437        let table = table?;
1438        if !table
1439            .table_info()
1440            .meta
1441            .options
1442            .extra_options
1443            .contains_key(LOGICAL_TABLE_METADATA_KEY)
1444        {
1445            // skip non-prometheus (non-metricengine) tables for __name__ query
1446            continue;
1447        }
1448
1449        let table_name = &table.table_info().name;
1450
1451        if let Some(matcher) = &name_matcher {
1452            match &matcher.op {
1453                MatchOp::Equal => {
1454                    if table_name == &matcher.value {
1455                        table_names.push(table_name.clone());
1456                    }
1457                }
1458                MatchOp::Re(reg) => {
1459                    if reg.is_match(table_name) {
1460                        table_names.push(table_name.clone());
1461                    }
1462                }
1463                _ => {
1464                    // != and !~ are not supported:
1465                    // vector must contains at least one non-empty matcher
1466                    table_names.push(table_name.clone());
1467                }
1468            }
1469        } else {
1470            table_names.push(table_name.clone());
1471        }
1472    }
1473
1474    table_names.sort_unstable();
1475    Ok(table_names)
1476}
1477
1478async fn retrieve_field_names(
1479    query_ctx: &QueryContext,
1480    manager: CatalogManagerRef,
1481    matches: Vec<String>,
1482) -> Result<HashSet<String>> {
1483    let mut field_columns = HashSet::new();
1484    let catalog = query_ctx.current_catalog();
1485    let schema = query_ctx.current_schema();
1486
1487    if matches.is_empty() {
1488        // query all tables if no matcher is provided
1489        while let Some(table) = manager
1490            .tables(catalog, &schema, Some(query_ctx))
1491            .next()
1492            .await
1493        {
1494            let table = table?;
1495            for column in table.field_columns() {
1496                field_columns.insert(column.name);
1497            }
1498        }
1499        return Ok(field_columns);
1500    }
1501
1502    for table_name in matches {
1503        let table = manager
1504            .table(catalog, &schema, &table_name, Some(query_ctx))
1505            .await?
1506            .with_context(|| TableNotFoundSnafu {
1507                catalog: catalog.to_string(),
1508                schema: schema.clone(),
1509                table: table_name.clone(),
1510            })?;
1511
1512        for column in table.field_columns() {
1513            field_columns.insert(column.name);
1514        }
1515    }
1516    Ok(field_columns)
1517}
1518
1519async fn retrieve_schema_names(
1520    query_ctx: &QueryContext,
1521    catalog_manager: CatalogManagerRef,
1522    matches: Vec<String>,
1523) -> Result<Vec<String>> {
1524    let mut schemas = Vec::new();
1525    let catalog = query_ctx.current_catalog();
1526
1527    let candidate_schemas = catalog_manager
1528        .schema_names(catalog, Some(query_ctx))
1529        .await?;
1530
1531    for schema in candidate_schemas {
1532        let mut found = true;
1533        for match_item in &matches {
1534            if let Some(table_name) = retrieve_metric_name_from_promql(match_item) {
1535                let exists = catalog_manager
1536                    .table_exists(catalog, &schema, &table_name, Some(query_ctx))
1537                    .await?;
1538                if !exists {
1539                    found = false;
1540                    break;
1541                }
1542            }
1543        }
1544
1545        if found {
1546            schemas.push(schema);
1547        }
1548    }
1549
1550    schemas.sort_unstable();
1551
1552    Ok(schemas)
1553}
1554
1555/// Try to parse and extract the name of referenced metric from the promql query.
1556///
1557/// Returns the metric name if exactly one unique metric is referenced, otherwise None.
1558/// Multiple references to the same metric are allowed.
1559fn retrieve_metric_name_from_promql(query: &str) -> Option<String> {
1560    let promql_expr = promql_parser::parser::parse(query).ok()?;
1561    promql_expr_to_metric_name(&promql_expr)
1562}
1563
1564#[derive(Debug, Default, Serialize, Deserialize)]
1565pub struct SeriesQuery {
1566    start: Option<String>,
1567    end: Option<String>,
1568    lookback: Option<String>,
1569    #[serde(flatten)]
1570    matches: Matches,
1571    db: Option<String>,
1572}
1573
1574#[axum_macros::debug_handler]
1575#[tracing::instrument(
1576    skip_all,
1577    fields(protocol = "prometheus", request_type = "series_query")
1578)]
1579pub async fn series_query(
1580    State(handler): State<PrometheusHandlerRef>,
1581    Query(params): Query<SeriesQuery>,
1582    Extension(mut query_ctx): Extension<QueryContext>,
1583    Form(form_params): Form<SeriesQuery>,
1584) -> PrometheusJsonResponse {
1585    let mut queries: Vec<String> = params.matches.0;
1586    if queries.is_empty() {
1587        queries = form_params.matches.0;
1588    }
1589    if queries.is_empty() {
1590        return PrometheusJsonResponse::error(
1591            StatusCode::Unsupported,
1592            "match[] parameter is required",
1593        );
1594    }
1595    let start = params
1596        .start
1597        .or(form_params.start)
1598        .unwrap_or_else(yesterday_rfc3339);
1599    let end = params
1600        .end
1601        .or(form_params.end)
1602        .unwrap_or_else(current_time_rfc3339);
1603    let lookback = params
1604        .lookback
1605        .or(form_params.lookback)
1606        .unwrap_or_else(|| DEFAULT_LOOKBACK_STRING.to_string());
1607
1608    // update catalog and schema in query context if necessary
1609    if let Some(db) = &params.db {
1610        let (catalog, schema) = parse_catalog_and_schema_from_db_string(db);
1611        try_update_catalog_schema(&mut query_ctx, &catalog, &schema);
1612    }
1613    let query_ctx = Arc::new(query_ctx);
1614
1615    let _timer = crate::metrics::METRIC_HTTP_PROMETHEUS_PROMQL_ELAPSED
1616        .with_label_values(&[query_ctx.get_db_string().as_str(), "series_query"])
1617        .start_timer();
1618
1619    let mut series = Vec::new();
1620    let mut merge_map = HashMap::new();
1621    for query in queries {
1622        let table_name = retrieve_metric_name_from_promql(&query).unwrap_or_default();
1623        let prom_query = PromQuery {
1624            query,
1625            start: start.clone(),
1626            end: end.clone(),
1627            // TODO: find a better value for step
1628            step: DEFAULT_LOOKBACK_STRING.to_string(),
1629            lookback: lookback.clone(),
1630            alias: None,
1631        };
1632        let result = handler.do_query(&prom_query, query_ctx.clone()).await;
1633
1634        handle_schema_err!(
1635            retrieve_series_from_query_result(
1636                result,
1637                &mut series,
1638                &query_ctx,
1639                &table_name,
1640                &handler.catalog_manager(),
1641                &mut merge_map,
1642            )
1643            .await
1644        );
1645    }
1646    let merge_map = merge_map
1647        .into_iter()
1648        .map(|(k, v)| (k, Value::from(v)))
1649        .collect();
1650    let mut resp = PrometheusJsonResponse::success(PrometheusResponse::Series(series));
1651    resp.resp_metrics = merge_map;
1652    resp
1653}
1654
1655#[derive(Debug, Default, Serialize, Deserialize)]
1656pub struct ParseQuery {
1657    query: Option<String>,
1658    db: Option<String>,
1659}
1660
1661#[axum_macros::debug_handler]
1662#[tracing::instrument(
1663    skip_all,
1664    fields(protocol = "prometheus", request_type = "parse_query")
1665)]
1666pub async fn parse_query(
1667    State(_handler): State<PrometheusHandlerRef>,
1668    Query(params): Query<ParseQuery>,
1669    Extension(_query_ctx): Extension<QueryContext>,
1670    Form(form_params): Form<ParseQuery>,
1671) -> PrometheusJsonResponse {
1672    if let Some(query) = params.query.or(form_params.query) {
1673        let ast = try_call_return_response!(promql_parser::parser::parse(&query));
1674        PrometheusJsonResponse::success(PrometheusResponse::ParseResult(ast))
1675    } else {
1676        PrometheusJsonResponse::error(StatusCode::InvalidArguments, "query is required")
1677    }
1678}
1679
1680#[cfg(test)]
1681mod tests {
1682    use std::collections::HashSet;
1683    use std::sync::Arc;
1684
1685    use catalog::memory::MemoryCatalogManager;
1686    use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
1687    use datatypes::prelude::ConcreteDataType;
1688    use datatypes::schema::{ColumnSchema, Schema};
1689    use promql_parser::parser::value::ValueType;
1690    use table::metadata::{TableInfoBuilder, TableMetaBuilder, TableType, TableVersion};
1691    use table::test_util::EmptyTable;
1692
1693    use super::*;
1694
1695    struct TestCase {
1696        name: &'static str,
1697        promql: &'static str,
1698        expected_metric: Option<&'static str>,
1699        expected_type: ValueType,
1700        should_error: bool,
1701    }
1702
1703    #[test]
1704    fn test_retrieve_metric_name_and_result_type() {
1705        let test_cases = &[
1706            // Single metric cases
1707            TestCase {
1708                name: "simple metric",
1709                promql: "cpu_usage",
1710                expected_metric: Some("cpu_usage"),
1711                expected_type: ValueType::Vector,
1712                should_error: false,
1713            },
1714            TestCase {
1715                name: "metric with selector",
1716                promql: r#"cpu_usage{instance="localhost"}"#,
1717                expected_metric: Some("cpu_usage"),
1718                expected_type: ValueType::Vector,
1719                should_error: false,
1720            },
1721            TestCase {
1722                name: "metric with range selector",
1723                promql: "cpu_usage[5m]",
1724                expected_metric: Some("cpu_usage"),
1725                expected_type: ValueType::Matrix,
1726                should_error: false,
1727            },
1728            TestCase {
1729                name: "metric with __name__ matcher",
1730                promql: r#"{__name__="cpu_usage"}"#,
1731                expected_metric: Some("cpu_usage"),
1732                expected_type: ValueType::Vector,
1733                should_error: false,
1734            },
1735            TestCase {
1736                name: "metric with unary operator",
1737                promql: "-cpu_usage",
1738                expected_metric: None,
1739                expected_type: ValueType::Vector,
1740                should_error: false,
1741            },
1742            // Aggregation and function cases
1743            TestCase {
1744                name: "metric with aggregation",
1745                promql: "sum(cpu_usage)",
1746                expected_metric: Some("cpu_usage"),
1747                expected_type: ValueType::Vector,
1748                should_error: false,
1749            },
1750            TestCase {
1751                name: "complex aggregation",
1752                promql: r#"sum by (instance) (cpu_usage{job="node"})"#,
1753                expected_metric: None,
1754                expected_type: ValueType::Vector,
1755                should_error: false,
1756            },
1757            TestCase {
1758                name: "complex aggregation",
1759                promql: r#"sum by (__name__) (cpu_usage{job="node"})"#,
1760                expected_metric: Some("cpu_usage"),
1761                expected_type: ValueType::Vector,
1762                should_error: false,
1763            },
1764            TestCase {
1765                name: "complex aggregation",
1766                promql: r#"sum without (instance) (cpu_usage{job="node"})"#,
1767                expected_metric: Some("cpu_usage"),
1768                expected_type: ValueType::Vector,
1769                should_error: false,
1770            },
1771            // Same metric binary operations
1772            TestCase {
1773                name: "same metric addition",
1774                promql: "cpu_usage + cpu_usage",
1775                expected_metric: None,
1776                expected_type: ValueType::Vector,
1777                should_error: false,
1778            },
1779            TestCase {
1780                name: "metric with scalar addition",
1781                promql: r#"sum(rate(cpu_usage{job="node"}[5m])) + 100"#,
1782                expected_metric: None,
1783                expected_type: ValueType::Vector,
1784                should_error: false,
1785            },
1786            // Multiple metrics cases
1787            TestCase {
1788                name: "different metrics addition",
1789                promql: "cpu_usage + memory_usage",
1790                expected_metric: None,
1791                expected_type: ValueType::Vector,
1792                should_error: false,
1793            },
1794            TestCase {
1795                name: "different metrics subtraction",
1796                promql: "network_in - network_out",
1797                expected_metric: None,
1798                expected_type: ValueType::Vector,
1799                should_error: false,
1800            },
1801            // Unless operator cases
1802            TestCase {
1803                name: "unless with different metrics",
1804                promql: "cpu_usage unless memory_usage",
1805                expected_metric: Some("cpu_usage"),
1806                expected_type: ValueType::Vector,
1807                should_error: false,
1808            },
1809            TestCase {
1810                name: "unless with same metric",
1811                promql: "cpu_usage unless cpu_usage",
1812                expected_metric: Some("cpu_usage"),
1813                expected_type: ValueType::Vector,
1814                should_error: false,
1815            },
1816            // Subquery cases
1817            TestCase {
1818                name: "basic subquery",
1819                promql: "cpu_usage[5m:1m]",
1820                expected_metric: Some("cpu_usage"),
1821                expected_type: ValueType::Matrix,
1822                should_error: false,
1823            },
1824            TestCase {
1825                name: "subquery with multiple metrics",
1826                promql: "(cpu_usage + memory_usage)[5m:1m]",
1827                expected_metric: None,
1828                expected_type: ValueType::Matrix,
1829                should_error: false,
1830            },
1831            // Literal values
1832            TestCase {
1833                name: "scalar value",
1834                promql: "42",
1835                expected_metric: None,
1836                expected_type: ValueType::Scalar,
1837                should_error: false,
1838            },
1839            TestCase {
1840                name: "string literal",
1841                promql: r#""hello world""#,
1842                expected_metric: None,
1843                expected_type: ValueType::String,
1844                should_error: false,
1845            },
1846            // Error cases
1847            TestCase {
1848                name: "invalid syntax",
1849                promql: "cpu_usage{invalid=",
1850                expected_metric: None,
1851                expected_type: ValueType::Vector,
1852                should_error: true,
1853            },
1854            TestCase {
1855                name: "empty query",
1856                promql: "",
1857                expected_metric: None,
1858                expected_type: ValueType::Vector,
1859                should_error: true,
1860            },
1861            TestCase {
1862                name: "malformed brackets",
1863                promql: "cpu_usage[5m",
1864                expected_metric: None,
1865                expected_type: ValueType::Vector,
1866                should_error: true,
1867            },
1868        ];
1869
1870        for test_case in test_cases {
1871            let result = retrieve_metric_name_and_result_type(test_case.promql);
1872
1873            if test_case.should_error {
1874                assert!(
1875                    result.is_err(),
1876                    "Test '{}' should have failed but succeeded with: {:?}",
1877                    test_case.name,
1878                    result
1879                );
1880            } else {
1881                let (metric_name, value_type) = result.unwrap_or_else(|e| {
1882                    panic!(
1883                        "Test '{}' should have succeeded but failed with error: {}",
1884                        test_case.name, e
1885                    )
1886                });
1887
1888                let expected_metric_name = test_case.expected_metric.map(|s| s.to_string());
1889                assert_eq!(
1890                    metric_name, expected_metric_name,
1891                    "Test '{}': metric name mismatch. Expected: {:?}, Got: {:?}",
1892                    test_case.name, expected_metric_name, metric_name
1893                );
1894
1895                assert_eq!(
1896                    value_type, test_case.expected_type,
1897                    "Test '{}': value type mismatch. Expected: {:?}, Got: {:?}",
1898                    test_case.name, test_case.expected_type, value_type
1899                );
1900            }
1901        }
1902    }
1903
1904    #[tokio::test]
1905    async fn test_get_all_column_names_uses_tag_columns() {
1906        let schema = Arc::new(Schema::new(vec![
1907            ColumnSchema::new(
1908                "greptime_timestamp",
1909                ConcreteDataType::timestamp_millisecond_datatype(),
1910                false,
1911            )
1912            .with_time_index(true),
1913            ColumnSchema::new("host", ConcreteDataType::string_datatype(), false),
1914            ColumnSchema::new("region", ConcreteDataType::string_datatype(), false),
1915            ColumnSchema::new("value", ConcreteDataType::float64_datatype(), true),
1916            ColumnSchema::new(
1917                DATA_SCHEMA_TSID_COLUMN_NAME,
1918                ConcreteDataType::uint64_datatype(),
1919                true,
1920            ),
1921        ]));
1922        let meta = TableMetaBuilder::empty()
1923            .schema(schema)
1924            .primary_key_indices(vec![1, 2, 4])
1925            .engine("metric".to_string())
1926            .next_column_id(5)
1927            .build()
1928            .unwrap();
1929        let table_info = TableInfoBuilder::default()
1930            .table_id(1024)
1931            .table_version(0 as TableVersion)
1932            .name("cpu_usage")
1933            .catalog_name(DEFAULT_CATALOG_NAME)
1934            .schema_name(DEFAULT_SCHEMA_NAME)
1935            .table_type(TableType::Base)
1936            .meta(meta)
1937            .build()
1938            .unwrap();
1939        let manager: CatalogManagerRef =
1940            MemoryCatalogManager::new_with_table(EmptyTable::from_table_info(&table_info));
1941
1942        let labels = get_all_column_names(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, &manager)
1943            .await
1944            .unwrap();
1945
1946        assert_eq!(
1947            labels,
1948            HashSet::from(["host".to_string(), "region".to_string()])
1949        );
1950    }
1951}