1use 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#[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#[derive(Debug, Default, Serialize, Deserialize, PartialEq)]
85pub struct PromSeriesMatrix {
86 pub metric: BTreeMap<String, String>,
87 pub values: Vec<(f64, String)>,
88}
89
90#[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#[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 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 }
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
232macro_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 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 if let Some(db) = ¶ms.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
338async 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 if let Some(db) = ¶ms.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 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
448async 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
475impl<'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
507macro_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 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(¶ms.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 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 let _ = labels.insert(METRIC_NAME.to_string());
563
564 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 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 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
621async 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 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
693async 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 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
750struct RowWriter {
757 template: HashMap<Column, Option<String>>,
760 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 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
1016fn 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 field_columns.iter().all(|c| c.is_null(row_index)) {
1053 continue;
1054 }
1055
1056 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
1077pub(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
1090pub(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 if metric_names.len() == 1 {
1104 metric_names.into_iter().next()
1105 } else {
1106 None
1107 }
1108}
1109
1110fn 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 | token::T_LOR | token::T_LUNLESS ) {
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
1198fn find_metric_name_not_equal_matchers(expr: &PromqlExpr) -> Option<Vec<Matcher>> {
1200 find_metric_name_and_matchers(expr, |name, matchers| {
1201 if name.is_some() {
1203 return None;
1204 }
1205
1206 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
1217fn 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(¶ms.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 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
1387fn 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 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 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 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 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 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
1555fn 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 if let Some(db) = ¶ms.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 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 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 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 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 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 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 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 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 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}