1use std::cmp::Ordering;
18use std::collections::HashMap;
19use std::collections::hash_map::DefaultHasher;
20use std::hash::{Hash, Hasher};
21
22use api::prom_store::remote::label_matcher::Type as MatcherType;
23use api::prom_store::remote::{Label, Query, Sample, TimeSeries, WriteRequest};
24use api::v1::RowInsertRequests;
25use arrow::array::{
26 Array, AsArray, DictionaryArray, LargeStringArray, StringArray, StringViewArray,
27};
28use arrow::datatypes::{Float64Type, TimestampMillisecondType, UInt32Type};
29use common_grpc::precision::Precision;
30use common_query::prelude::{greptime_timestamp, greptime_value};
31use common_recordbatch::{RecordBatch, RecordBatches};
32use common_telemetry::{tracing, warn};
33use datafusion::dataframe::DataFrame;
34use datafusion::prelude::{Expr, col, lit, regexp_match};
35use datafusion_common::ScalarValue;
36use datafusion_expr::LogicalPlan;
37use openmetrics_parser::{MetricsExposition, PrometheusType, PrometheusValue};
38use snafu::{OptionExt, ResultExt, ensure};
39use snap::raw::{Decoder, Encoder};
40
41use crate::error::{self, Result};
42use crate::prom_remote_write::REMOTE_WRITE_V1_VERSION;
43use crate::row_writer::{self, MultiTableData};
44
45pub const METRIC_NAME_LABEL: &str = "__name__";
46pub const METRIC_NAME_LABEL_BYTES: &[u8] = b"__name__";
47
48pub const DATABASE_LABEL: &str = "x_greptime_database";
50pub const DATABASE_LABEL_BYTES: &[u8] = b"x_greptime_database";
51pub const DATABASE_LABEL_ALT: &str = "__database__";
52pub const DATABASE_LABEL_ALT_BYTES: &[u8] = b"__database__";
53
54#[deprecated(note = "use DATABASE_LABEL instead")]
56pub const SCHEMA_LABEL: &str = "__schema__";
57#[deprecated(note = "use DATABASE_LABEL_BYTES instead")]
58pub const SCHEMA_LABEL_BYTES: &[u8] = b"__schema__";
59
60pub const PHYSICAL_TABLE_LABEL: &str = "x_greptime_physical_table";
62pub const PHYSICAL_TABLE_LABEL_BYTES: &[u8] = b"x_greptime_physical_table";
63pub const PHYSICAL_TABLE_LABEL_ALT: &str = "__physical_table__";
64pub const PHYSICAL_TABLE_LABEL_ALT_BYTES: &[u8] = b"__physical_table__";
65
66pub const FIELD_NAME_LABEL: &str = "__field__";
68
69#[allow(deprecated)]
71pub fn is_remote_write_special_label(label: &str) -> bool {
72 label == DATABASE_LABEL
73 || label == DATABASE_LABEL_ALT
74 || label == PHYSICAL_TABLE_LABEL
75 || label == PHYSICAL_TABLE_LABEL_ALT
76 || label == SCHEMA_LABEL
77}
78
79#[allow(deprecated)]
80pub fn is_remote_read_special_label(label: &str) -> bool {
81 label == METRIC_NAME_LABEL
82 || label == DATABASE_LABEL
83 || label == DATABASE_LABEL_ALT
84 || label == SCHEMA_LABEL
85}
86
87#[allow(deprecated)]
89pub fn is_database_selection_label(label: &str) -> bool {
90 label == DATABASE_LABEL || label == DATABASE_LABEL_ALT || label == SCHEMA_LABEL
91}
92
93pub fn is_physical_table_selection_label(label: &str) -> bool {
95 label == PHYSICAL_TABLE_LABEL || label == PHYSICAL_TABLE_LABEL_ALT
96}
97
98pub struct Metrics {
100 pub exposition: MetricsExposition<PrometheusType, PrometheusValue>,
101}
102
103pub fn table_name(q: &Query) -> Result<String> {
105 let mut matchers = q
106 .matchers
107 .iter()
108 .filter(|matcher| matcher.name == METRIC_NAME_LABEL);
109 let matcher = matchers
110 .next()
111 .context(error::InvalidPromRemoteRequestSnafu {
112 msg: "missing '__name__' label in timeseries",
113 })?;
114
115 if matcher.r#type != MatcherType::Eq as i32
116 || matcher.value.is_empty()
117 || matchers.next().is_some()
118 {
119 return Err(error::InvalidPromRemoteRequestSnafu {
120 msg: "expected exactly one non-empty equality matcher for '__name__'".to_string(),
121 }
122 .build());
123 }
124
125 Ok(matcher.value.clone())
126}
127
128pub fn extract_schema_from_query(query: &Query) -> Option<String> {
130 query
131 .matchers
132 .iter()
133 .find(|matcher| {
134 is_database_selection_label(&matcher.name) && matcher.r#type == MatcherType::Eq as i32
135 })
136 .map(|matcher| matcher.value.clone())
137}
138
139#[tracing::instrument(skip_all)]
141pub fn query_to_plan(
142 dataframe: DataFrame,
143 q: &Query,
144 timestamp_column_name: &str,
145) -> Result<LogicalPlan> {
146 let start_timestamp_ms = q.start_timestamp_ms;
147 let end_timestamp_ms = q.end_timestamp_ms;
148
149 let label_matches = &q.matchers;
150
151 let mut conditions = Vec::with_capacity(label_matches.len() + 1);
152
153 conditions
154 .push(col(timestamp_column_name).gt_eq(lit_timestamp_millisecond(start_timestamp_ms)));
155 conditions.push(col(timestamp_column_name).lt_eq(lit_timestamp_millisecond(end_timestamp_ms)));
156
157 for m in label_matches {
158 let name = &m.name;
159
160 if is_remote_read_special_label(name) {
161 continue;
162 }
163
164 let value = &m.value;
165 let m_type = MatcherType::try_from(m.r#type).map_err(|e| {
166 error::InvalidPromRemoteRequestSnafu {
167 msg: format!("invalid LabelMatcher type, decode error: {e}",),
168 }
169 .build()
170 })?;
171
172 match m_type {
173 MatcherType::Eq => {
174 conditions.push(col(name).eq(lit(value)));
175 }
176 MatcherType::Neq => {
177 conditions.push(col(name).not_eq(lit(value)));
178 }
179 MatcherType::Re => {
181 conditions.push(regexp_match(col(name), lit(value), None).is_not_null());
182 }
183 MatcherType::Nre => {
185 conditions.push(regexp_match(col(name), lit(value), None).is_null());
186 }
187 }
188 }
189
190 let conditions = conditions.into_iter().reduce(Expr::and).unwrap();
192
193 let dataframe = dataframe
194 .filter(conditions)
195 .context(error::DataFrameSnafu)?;
196
197 Ok(dataframe.into_parts().1)
198}
199
200#[inline]
201fn new_label(name: String, value: String) -> Label {
202 Label { name, value }
203}
204
205fn lit_timestamp_millisecond(ts: i64) -> Expr {
206 Expr::Literal(ScalarValue::TimestampMillisecond(Some(ts), None), None)
207}
208
209fn compare_timeseries_labels(left: &[Label], right: &[Label]) -> Ordering {
211 let ordering = left.len().cmp(&right.len());
212 if ordering != Ordering::Equal {
213 return ordering;
214 }
215
216 for (left, right) in left.iter().zip(right) {
217 let ordering = left.name.cmp(&right.name);
218 if ordering != Ordering::Equal {
219 return ordering;
220 }
221
222 let ordering = left.value.cmp(&right.value);
223 if ordering != Ordering::Equal {
224 return ordering;
225 }
226 }
227
228 Ordering::Equal
229}
230
231enum LabelValues<'a> {
232 Utf8(&'a StringArray),
233 LargeUtf8(&'a LargeStringArray),
234 Utf8View(&'a StringViewArray),
235 DictionaryUtf8 {
236 dictionary: &'a DictionaryArray<UInt32Type>,
237 values: &'a StringArray,
238 },
239 Other(Vec<Option<String>>),
240}
241
242impl LabelValues<'_> {
243 fn value(&self, row: usize) -> Option<&str> {
244 match self {
245 Self::Utf8(values) => values.is_valid(row).then(|| values.value(row)),
246 Self::LargeUtf8(values) => values.is_valid(row).then(|| values.value(row)),
247 Self::Utf8View(values) => values.is_valid(row).then(|| values.value(row)),
248 Self::DictionaryUtf8 { dictionary, values } => dictionary
249 .key(row)
250 .and_then(|key| values.is_valid(key).then(|| values.value(key))),
251 Self::Other(values) => values.get(row).and_then(Option::as_deref),
252 }
253 }
254}
255
256fn row_labels<'a>(
257 columns: &'a [LabelColumn<'a>],
258 row: usize,
259) -> impl Iterator<Item = (&'a str, &'a str)> {
260 columns
261 .iter()
262 .filter_map(move |column| column.values.value(row).map(|value| (column.name, value)))
263}
264
265struct LabelColumn<'a> {
266 name: &'a str,
267 values: LabelValues<'a>,
268}
269
270fn label_columns<'a>(
271 recordbatch: &'a RecordBatch,
272 timestamp_column_name: &str,
273 value_column_name: &str,
274) -> Result<Vec<LabelColumn<'a>>> {
275 recordbatch
276 .schema
277 .column_schemas()
278 .iter()
279 .enumerate()
280 .filter(|(_, column_schema)| {
281 column_schema.name != timestamp_column_name && column_schema.name != value_column_name
282 })
283 .map(|(index, column_schema)| {
284 let array = recordbatch.column(index);
285 let values = match array.data_type() {
286 arrow::datatypes::DataType::Utf8 => LabelValues::Utf8(array.as_string::<i32>()),
287 arrow::datatypes::DataType::LargeUtf8 => {
288 LabelValues::LargeUtf8(array.as_string::<i64>())
289 }
290 arrow::datatypes::DataType::Utf8View => {
291 LabelValues::Utf8View(array.as_string_view())
292 }
293 arrow::datatypes::DataType::Dictionary(key, value)
294 if key.as_ref() == &arrow::datatypes::DataType::UInt32
295 && value.as_ref() == &arrow::datatypes::DataType::Utf8 =>
296 {
297 let dictionary = array.as_dictionary::<UInt32Type>();
298 LabelValues::DictionaryUtf8 {
299 dictionary,
300 values: dictionary.values().as_string::<i32>(),
301 }
302 }
303 _ => {
304 let values = recordbatch.iter_column_as_string(index).collect::<Vec<_>>();
305 ensure!(
306 values.len() == recordbatch.num_rows(),
307 error::InvalidPromRemoteReadQueryResultSnafu {
308 msg: format!(
309 "Cannot convert label column '{}' of datatype {:?} to string",
310 column_schema.name,
311 array.data_type()
312 ),
313 }
314 );
315 LabelValues::Other(values)
316 }
317 };
318 Ok(LabelColumn {
319 name: &column_schema.name,
320 values,
321 })
322 })
323 .collect()
324}
325
326fn hash_timeseries(columns: &[LabelColumn<'_>], row: usize) -> u64 {
327 let mut hasher = DefaultHasher::new();
328
329 for (name, value) in row_labels(columns, row) {
330 name.hash(&mut hasher);
331 value.hash(&mut hasher);
332 }
333
334 hasher.finish()
335}
336
337fn matches_timeseries(labels: &[Label], columns: &[LabelColumn<'_>], row: usize) -> bool {
338 let mut labels = labels.iter().skip(1);
339 for (name, value) in row_labels(columns, row) {
340 let Some(label) = labels.next() else {
341 return false;
342 };
343 if label.name != name || label.value != value {
344 return false;
345 }
346 }
347
348 labels.next().is_none()
349}
350
351fn new_timeseries(table: &str, columns: &[LabelColumn<'_>], row: usize) -> TimeSeries {
352 let mut labels = Vec::with_capacity(columns.len() + 1);
353 labels.push(new_label(METRIC_NAME_LABEL.to_string(), table.to_string()));
354
355 for (name, value) in row_labels(columns, row) {
356 labels.push(new_label(name.to_string(), value.to_string()));
357 }
358
359 TimeSeries {
360 labels,
361 ..Default::default()
362 }
363}
364
365pub fn recordbatches_to_timeseries(
366 table_name: &str,
367 timestamp_column_name: &str,
368 value_column_name: &str,
369 recordbatches: RecordBatches,
370) -> Result<Vec<TimeSeries>> {
371 Ok(recordbatches
372 .take()
373 .into_iter()
374 .map(|x| recordbatch_to_timeseries(table_name, timestamp_column_name, value_column_name, x))
375 .collect::<Result<Vec<_>>>()?
376 .into_iter()
377 .flatten()
378 .collect())
379}
380
381fn recordbatch_to_timeseries(
382 table: &str,
383 timestamp_column_name: &str,
384 value_column_name: &str,
385 recordbatch: RecordBatch,
386) -> Result<Vec<TimeSeries>> {
387 let ts_column = recordbatch
388 .column_by_name(timestamp_column_name)
389 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
390 msg: format!("missing timestamp column '{timestamp_column_name}' in query result"),
391 })?;
392 let ts_column = ts_column
393 .as_primitive_opt::<TimestampMillisecondType>()
394 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
395 msg: format!(
396 "Expect timestamp column of datatype Timestamp(Millisecond), actual {:?}",
397 ts_column.data_type()
398 ),
399 })?;
400
401 let field_column = recordbatch
404 .column_by_name(value_column_name)
405 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
406 msg: format!("missing value column '{value_column_name}' in query result"),
407 })?;
408 let field_column = field_column
409 .as_primitive_opt::<Float64Type>()
410 .with_context(|| error::InvalidPromRemoteReadQueryResultSnafu {
411 msg: format!(
412 "Expect value column of datatype Float64, actual {:?}",
413 field_column.data_type()
414 ),
415 })?;
416
417 let columns = label_columns(&recordbatch, timestamp_column_name, value_column_name)?;
418 let mut timeseries: Vec<TimeSeries> = Vec::new();
419 let mut timeseries_by_hash: HashMap<u64, Vec<usize>> = HashMap::new();
420 let mut previous_timeseries: Option<usize> = None;
421
422 for row in 0..recordbatch.num_rows() {
423 let timeseries_index = match previous_timeseries {
424 Some(index) if matches_timeseries(×eries[index].labels, &columns, row) => index,
425 _ => {
426 let hash = hash_timeseries(&columns, row);
427 let candidates = timeseries_by_hash.entry(hash).or_default();
428 match candidates
429 .iter()
430 .copied()
431 .find(|index| matches_timeseries(×eries[*index].labels, &columns, row))
432 {
433 Some(index) => index,
434 None => {
435 let index = timeseries.len();
436 timeseries.push(new_timeseries(table, &columns, row));
437 candidates.push(index);
438 index
439 }
440 }
441 }
442 };
443 previous_timeseries = Some(timeseries_index);
444
445 if ts_column.is_null(row) || field_column.is_null(row) {
446 continue;
447 }
448
449 let value = field_column.value(row);
450 let timestamp = ts_column.value(row);
451 let sample = Sample { value, timestamp };
452
453 timeseries[timeseries_index].samples.push(sample);
454 }
455
456 timeseries
457 .sort_unstable_by(|left, right| compare_timeseries_labels(&left.labels, &right.labels));
458 Ok(timeseries)
459}
460
461pub fn to_grpc_row_insert_requests(request: &WriteRequest) -> Result<(RowInsertRequests, usize)> {
462 let _timer = crate::metrics::METRIC_HTTP_PROM_STORE_CODEC_ELAPSED
463 .with_label_values(&["convert", REMOTE_WRITE_V1_VERSION])
464 .start_timer();
465
466 let mut multi_table_data = MultiTableData::new();
467
468 for series in &request.timeseries {
469 let table_name = &series
470 .labels
471 .iter()
472 .find(|label| {
473 label.name == METRIC_NAME_LABEL
475 })
476 .context(error::InvalidPromRemoteRequestSnafu {
477 msg: "missing '__name__' label in time-series",
478 })?
479 .value;
480
481 let num_columns = series.labels.len() + 1;
484
485 let table_data = multi_table_data.get_or_default_table_data(
486 table_name,
487 num_columns,
488 series.samples.len(),
489 );
490
491 let kvs = series.labels.iter().filter_map(|label| {
493 if label.name == METRIC_NAME_LABEL {
494 None
495 } else {
496 Some((label.name.clone(), label.value.clone()))
497 }
498 });
499
500 if series.samples.len() == 1 {
501 let mut one_row = table_data.alloc_one_row();
502
503 row_writer::write_tags(table_data, kvs, &mut one_row)?;
504 row_writer::write_f64(
506 table_data,
507 greptime_value(),
508 series.samples[0].value,
509 &mut one_row,
510 )?;
511 row_writer::write_ts_to_millis(
513 table_data,
514 greptime_timestamp(),
515 Some(series.samples[0].timestamp),
516 Precision::Millisecond,
517 &mut one_row,
518 )?;
519
520 table_data.add_row(one_row);
521 } else {
522 for Sample { value, timestamp } in &series.samples {
523 let mut one_row = table_data.alloc_one_row();
524
525 let kvs = kvs.clone();
527 row_writer::write_tags(table_data, kvs, &mut one_row)?;
528 row_writer::write_f64(table_data, greptime_value(), *value, &mut one_row)?;
530 row_writer::write_ts_to_millis(
532 table_data,
533 greptime_timestamp(),
534 Some(*timestamp),
535 Precision::Millisecond,
536 &mut one_row,
537 )?;
538
539 table_data.add_row(one_row);
540 }
541 }
542
543 if !series.histograms.is_empty() {
544 warn!("Native histograms are not supported yet, data ignored");
545 }
546 }
547
548 Ok(multi_table_data.into_row_insert_requests())
549}
550
551#[inline]
552pub fn snappy_decompress(buf: &[u8]) -> Result<Vec<u8>> {
553 let mut decoder = Decoder::new();
554 decoder
555 .decompress_vec(buf)
556 .context(error::DecompressSnappyPromRemoteRequestSnafu)
557}
558
559#[inline]
560pub fn snappy_compress(buf: &[u8]) -> Result<Vec<u8>> {
561 let mut encoder = Encoder::new();
562 encoder
563 .compress_vec(buf)
564 .context(error::CompressPromRemoteRequestSnafu)
565}
566
567#[inline]
568pub fn zstd_decompress(buf: &[u8]) -> Result<Vec<u8>> {
569 zstd::stream::decode_all(buf).context(error::DecompressZstdPromRemoteRequestSnafu)
570}
571
572pub fn mock_timeseries() -> Vec<TimeSeries> {
575 vec![
576 TimeSeries {
577 labels: vec![
578 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
579 new_label("job".to_string(), "spark".to_string()),
580 ],
581 samples: vec![
582 Sample {
583 value: 1.0f64,
584 timestamp: 1000,
585 },
586 Sample {
587 value: 2.0f64,
588 timestamp: 2000,
589 },
590 ],
591 ..Default::default()
592 },
593 TimeSeries {
594 labels: vec![
595 new_label(METRIC_NAME_LABEL.to_string(), "metric2".to_string()),
596 new_label("instance".to_string(), "test_host1".to_string()),
597 new_label("idc".to_string(), "z001".to_string()),
598 ],
599 samples: vec![
600 Sample {
601 value: 3.0f64,
602 timestamp: 1000,
603 },
604 Sample {
605 value: 4.0f64,
606 timestamp: 2000,
607 },
608 ],
609 ..Default::default()
610 },
611 TimeSeries {
612 labels: vec![
613 new_label(METRIC_NAME_LABEL.to_string(), "metric3".to_string()),
614 new_label("idc".to_string(), "z002".to_string()),
615 new_label("app".to_string(), "biz".to_string()),
616 ],
617 samples: vec![
618 Sample {
619 value: 5.0f64,
620 timestamp: 1000,
621 },
622 Sample {
623 value: 6.0f64,
624 timestamp: 2000,
625 },
626 Sample {
627 value: 7.0f64,
628 timestamp: 3000,
629 },
630 ],
631 ..Default::default()
632 },
633 ]
634}
635
636pub fn mock_timeseries_new_label() -> Vec<TimeSeries> {
638 let ts_demo_metrics = TimeSeries {
639 labels: vec![
640 new_label(METRIC_NAME_LABEL.to_string(), "demo_metrics".to_string()),
641 new_label("idc".to_string(), "idc3".to_string()),
642 new_label("new_label1".to_string(), "foo".to_string()),
643 ],
644 samples: vec![Sample {
645 value: 42.0,
646 timestamp: 3000,
647 }],
648 ..Default::default()
649 };
650 let ts_multi_labels = TimeSeries {
651 labels: vec![
652 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
653 new_label("idc".to_string(), "idc4".to_string()),
654 new_label("env".to_string(), "prod".to_string()),
655 new_label("host".to_string(), "host9".to_string()),
656 new_label("new_label2".to_string(), "bar".to_string()),
657 ],
658 samples: vec![Sample {
659 value: 99.0,
660 timestamp: 4000,
661 }],
662 ..Default::default()
663 };
664
665 vec![ts_demo_metrics, ts_multi_labels]
666}
667
668pub fn mock_timeseries_special_labels() -> Vec<TimeSeries> {
670 let idc3_schema = TimeSeries {
671 labels: vec![
672 new_label(METRIC_NAME_LABEL.to_string(), "idc3_lo_table".to_string()),
673 new_label(DATABASE_LABEL.to_string(), "idc3".to_string()),
674 new_label(PHYSICAL_TABLE_LABEL.to_string(), "f1".to_string()),
675 ],
676 samples: vec![Sample {
677 value: 42.0,
678 timestamp: 3000,
679 }],
680 ..Default::default()
681 };
682 let idc4_schema = TimeSeries {
683 labels: vec![
684 new_label(
685 METRIC_NAME_LABEL.to_string(),
686 "idc4_local_table".to_string(),
687 ),
688 new_label(DATABASE_LABEL.to_string(), "idc4".to_string()),
689 new_label(PHYSICAL_TABLE_LABEL.to_string(), "f2".to_string()),
690 ],
691 samples: vec![Sample {
692 value: 99.0,
693 timestamp: 4000,
694 }],
695 ..Default::default()
696 };
697
698 vec![idc3_schema, idc4_schema]
699}
700
701#[cfg(test)]
702mod tests {
703 use std::sync::Arc;
704
705 use api::prom_store::remote::LabelMatcher;
706 use api::v1::{ColumnDataType, Row, SemanticType};
707 use arrow::array::{
708 DictionaryArray, Float64Array, StringArray, TimestampMillisecondArray, UInt32Array,
709 };
710 use arrow::datatypes::{DataType as ArrowDataType, Field, Schema as ArrowSchema, UInt32Type};
711 use common_recordbatch::DfRecordBatch;
712 use datafusion::prelude::SessionContext;
713 use datatypes::data_type::ConcreteDataType;
714 use datatypes::schema::{ColumnSchema, Schema};
715 use datatypes::vectors::{
716 Float64Vector, Int32Vector, StringVector, TimestampMillisecondVector,
717 };
718 use table::table::adapter::DfTableProviderAdapter;
719 use table::test_util::MemTable;
720
721 use super::*;
722
723 const EQ_TYPE: i32 = MatcherType::Eq as i32;
724 const NEQ_TYPE: i32 = MatcherType::Neq as i32;
725 const RE_TYPE: i32 = MatcherType::Re as i32;
726
727 #[test]
728 fn test_table_name() {
729 let q = Query {
730 start_timestamp_ms: 1000,
731 end_timestamp_ms: 2000,
732 matchers: vec![],
733 ..Default::default()
734 };
735 let err = table_name(&q).unwrap_err();
736 assert!(matches!(err, error::Error::InvalidPromRemoteRequest { .. }));
737
738 let q = Query {
739 start_timestamp_ms: 1000,
740 end_timestamp_ms: 2000,
741 matchers: vec![LabelMatcher {
742 name: METRIC_NAME_LABEL.to_string(),
743 value: "test".to_string(),
744 r#type: EQ_TYPE,
745 }],
746 ..Default::default()
747 };
748 assert_eq!("test", table_name(&q).unwrap());
749
750 for matchers in [
751 vec![LabelMatcher {
752 name: METRIC_NAME_LABEL.to_string(),
753 value: "test.*".to_string(),
754 r#type: RE_TYPE,
755 }],
756 vec![LabelMatcher {
757 name: METRIC_NAME_LABEL.to_string(),
758 value: String::new(),
759 r#type: EQ_TYPE,
760 }],
761 vec![
762 LabelMatcher {
763 name: METRIC_NAME_LABEL.to_string(),
764 value: "test".to_string(),
765 r#type: EQ_TYPE,
766 },
767 LabelMatcher {
768 name: METRIC_NAME_LABEL.to_string(),
769 value: "other".to_string(),
770 r#type: EQ_TYPE,
771 },
772 ],
773 ] {
774 let q = Query {
775 matchers,
776 ..Default::default()
777 };
778 assert!(matches!(
779 table_name(&q),
780 Err(error::Error::InvalidPromRemoteRequest { .. })
781 ));
782 }
783 }
784
785 #[test]
786 #[allow(deprecated)]
787 fn test_extract_schema_from_query() {
788 let query = Query::default();
789 assert_eq!(None, extract_schema_from_query(&query));
790
791 for label in [DATABASE_LABEL, DATABASE_LABEL_ALT, SCHEMA_LABEL] {
792 let query = Query {
793 matchers: vec![LabelMatcher {
794 name: label.to_string(),
795 value: "selected_schema".to_string(),
796 r#type: EQ_TYPE,
797 }],
798 ..Default::default()
799 };
800 assert_eq!(
801 Some("selected_schema".to_string()),
802 extract_schema_from_query(&query)
803 );
804 }
805
806 let query = Query {
807 matchers: vec![LabelMatcher {
808 name: DATABASE_LABEL.to_string(),
809 value: "selected_schema".to_string(),
810 r#type: NEQ_TYPE,
811 }],
812 ..Default::default()
813 };
814 assert_eq!(None, extract_schema_from_query(&query));
815 }
816
817 #[test]
818 fn test_query_to_plan() {
819 let q = Query {
820 start_timestamp_ms: 1000,
821 end_timestamp_ms: 2000,
822 matchers: vec![LabelMatcher {
823 name: METRIC_NAME_LABEL.to_string(),
824 value: "test".to_string(),
825 r#type: EQ_TYPE,
826 }],
827 ..Default::default()
828 };
829
830 let schema = Arc::new(Schema::new(vec![
831 ColumnSchema::new(
832 greptime_timestamp(),
833 ConcreteDataType::timestamp_millisecond_datatype(),
834 true,
835 ),
836 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
837 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
838 ColumnSchema::new("job", ConcreteDataType::string_datatype(), true),
839 ]));
840 let recordbatch = RecordBatch::new(
841 schema,
842 vec![
843 Arc::new(TimestampMillisecondVector::from_vec(vec![1000])) as _,
844 Arc::new(Float64Vector::from_vec(vec![3.0])) as _,
845 Arc::new(StringVector::from(vec!["host1"])) as _,
846 Arc::new(StringVector::from(vec!["job"])) as _,
847 ],
848 )
849 .unwrap();
850
851 let ctx = SessionContext::new();
852 let table = MemTable::table("test", recordbatch);
853 let table_provider = Arc::new(DfTableProviderAdapter::new(table));
854
855 let dataframe = ctx.read_table(table_provider.clone()).unwrap();
856 let plan = query_to_plan(dataframe, &q, greptime_timestamp()).unwrap();
857 let display_string = format!("{}", plan.display_indent());
858
859 let ts_col = greptime_timestamp();
860 let expected = format!(
861 "Filter: ?table?.{} >= TimestampMillisecond(1000, None) AND ?table?.{} <= TimestampMillisecond(2000, None)\n TableScan: ?table?",
862 ts_col, ts_col
863 );
864 assert_eq!(expected, display_string);
865
866 let q = Query {
867 start_timestamp_ms: 1000,
868 end_timestamp_ms: 2000,
869 matchers: vec![
870 LabelMatcher {
871 name: METRIC_NAME_LABEL.to_string(),
872 value: "test".to_string(),
873 r#type: EQ_TYPE,
874 },
875 LabelMatcher {
876 name: "job".to_string(),
877 value: "*prom*".to_string(),
878 r#type: RE_TYPE,
879 },
880 LabelMatcher {
881 name: "instance".to_string(),
882 value: "localhost".to_string(),
883 r#type: NEQ_TYPE,
884 },
885 ],
886 ..Default::default()
887 };
888
889 let dataframe = ctx.read_table(table_provider).unwrap();
890 let plan = query_to_plan(dataframe, &q, greptime_timestamp()).unwrap();
891 let display_string = format!("{}", plan.display_indent());
892
893 let ts_col = greptime_timestamp();
894 let expected = format!(
895 "Filter: ?table?.{} >= TimestampMillisecond(1000, None) AND ?table?.{} <= TimestampMillisecond(2000, None) AND regexp_match(?table?.job, Utf8(\"*prom*\")) IS NOT NULL AND ?table?.instance != Utf8(\"localhost\")\n TableScan: ?table?",
896 ts_col, ts_col
897 );
898 assert_eq!(expected, display_string);
899 }
900
901 fn column_schemas_with(
902 mut kts_iter: Vec<(&str, ColumnDataType, SemanticType)>,
903 ) -> Vec<api::v1::ColumnSchema> {
904 kts_iter.push((
905 greptime_value(),
906 ColumnDataType::Float64,
907 SemanticType::Field,
908 ));
909 kts_iter.push((
910 greptime_timestamp(),
911 ColumnDataType::TimestampMillisecond,
912 SemanticType::Timestamp,
913 ));
914
915 kts_iter
916 .into_iter()
917 .map(|(k, t, s)| api::v1::ColumnSchema {
918 column_name: k.to_string(),
919 datatype: t as i32,
920 semantic_type: s as i32,
921 ..Default::default()
922 })
923 .collect()
924 }
925
926 fn make_row_with_label(l1: &str, value: f64, timestamp: i64) -> Row {
927 Row {
928 values: vec![
929 api::v1::Value {
930 value_data: Some(api::v1::value::ValueData::StringValue(l1.to_string())),
931 },
932 api::v1::Value {
933 value_data: Some(api::v1::value::ValueData::F64Value(value)),
934 },
935 api::v1::Value {
936 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(
937 timestamp,
938 )),
939 },
940 ],
941 }
942 }
943
944 fn make_row_with_2_labels(l1: &str, l2: &str, value: f64, timestamp: i64) -> Row {
945 Row {
946 values: vec![
947 api::v1::Value {
948 value_data: Some(api::v1::value::ValueData::StringValue(l1.to_string())),
949 },
950 api::v1::Value {
951 value_data: Some(api::v1::value::ValueData::StringValue(l2.to_string())),
952 },
953 api::v1::Value {
954 value_data: Some(api::v1::value::ValueData::F64Value(value)),
955 },
956 api::v1::Value {
957 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(
958 timestamp,
959 )),
960 },
961 ],
962 }
963 }
964
965 #[test]
966 fn test_write_request_to_row_insert_exprs() {
967 let write_request = WriteRequest {
968 timeseries: mock_timeseries(),
969 ..Default::default()
970 };
971
972 let mut exprs = to_grpc_row_insert_requests(&write_request)
973 .unwrap()
974 .0
975 .inserts;
976 exprs.sort_unstable_by(|l, r| l.table_name.cmp(&r.table_name));
977 assert_eq!(3, exprs.len());
978 assert_eq!("metric1", exprs[0].table_name);
979 assert_eq!("metric2", exprs[1].table_name);
980 assert_eq!("metric3", exprs[2].table_name);
981
982 let rows = exprs[0].rows.as_ref().unwrap();
983 let schema = &rows.schema;
984 let rows = &rows.rows;
985 assert_eq!(2, rows.len());
986 assert_eq!(3, schema.len());
987 assert_eq!(
988 column_schemas_with(vec![("job", ColumnDataType::String, SemanticType::Tag)]),
989 *schema
990 );
991 assert_eq!(
992 &vec![
993 make_row_with_label("spark", 1.0, 1000),
994 make_row_with_label("spark", 2.0, 2000),
995 ],
996 rows
997 );
998
999 let rows = exprs[1].rows.as_ref().unwrap();
1000 let schema = &rows.schema;
1001 let rows = &rows.rows;
1002 assert_eq!(2, rows.len());
1003 assert_eq!(4, schema.len());
1004 assert_eq!(
1005 column_schemas_with(vec![
1006 ("instance", ColumnDataType::String, SemanticType::Tag),
1007 ("idc", ColumnDataType::String, SemanticType::Tag)
1008 ]),
1009 *schema
1010 );
1011 assert_eq!(
1012 &vec![
1013 make_row_with_2_labels("test_host1", "z001", 3.0, 1000),
1014 make_row_with_2_labels("test_host1", "z001", 4.0, 2000),
1015 ],
1016 rows
1017 );
1018
1019 let rows = exprs[2].rows.as_ref().unwrap();
1020 let schema = &rows.schema;
1021 let rows = &rows.rows;
1022 assert_eq!(3, rows.len());
1023 assert_eq!(4, schema.len());
1024 assert_eq!(
1025 column_schemas_with(vec![
1026 ("idc", ColumnDataType::String, SemanticType::Tag),
1027 ("app", ColumnDataType::String, SemanticType::Tag)
1028 ]),
1029 *schema
1030 );
1031 assert_eq!(
1032 &vec![
1033 make_row_with_2_labels("z002", "biz", 5.0, 1000),
1034 make_row_with_2_labels("z002", "biz", 6.0, 2000),
1035 make_row_with_2_labels("z002", "biz", 7.0, 3000),
1036 ],
1037 rows
1038 );
1039 }
1040
1041 #[test]
1042 fn test_recordbatches_to_timeseries() {
1043 let schema = Arc::new(Schema::new(vec![
1044 ColumnSchema::new(
1045 greptime_timestamp(),
1046 ConcreteDataType::timestamp_millisecond_datatype(),
1047 true,
1048 ),
1049 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1050 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
1051 ]));
1052
1053 let recordbatches = RecordBatches::try_new(
1054 schema.clone(),
1055 vec![
1056 RecordBatch::new(
1057 schema.clone(),
1058 vec![
1059 Arc::new(TimestampMillisecondVector::from_vec(vec![1000])) as _,
1060 Arc::new(Float64Vector::from_vec(vec![3.0])) as _,
1061 Arc::new(StringVector::from(vec!["host1"])) as _,
1062 ],
1063 )
1064 .unwrap(),
1065 RecordBatch::new(
1066 schema,
1067 vec![
1068 Arc::new(TimestampMillisecondVector::from_vec(vec![2000])) as _,
1069 Arc::new(Float64Vector::from_vec(vec![7.0])) as _,
1070 Arc::new(StringVector::from(vec!["host2"])) as _,
1071 ],
1072 )
1073 .unwrap(),
1074 ],
1075 )
1076 .unwrap();
1077
1078 let timeseries = recordbatches_to_timeseries(
1079 "metric1",
1080 greptime_timestamp(),
1081 greptime_value(),
1082 recordbatches,
1083 )
1084 .unwrap();
1085 assert_eq!(2, timeseries.len());
1086
1087 assert_eq!(
1088 vec![
1089 Label {
1090 name: METRIC_NAME_LABEL.to_string(),
1091 value: "metric1".to_string(),
1092 },
1093 Label {
1094 name: "instance".to_string(),
1095 value: "host1".to_string(),
1096 },
1097 ],
1098 timeseries[0].labels
1099 );
1100
1101 assert_eq!(
1102 timeseries[0].samples,
1103 vec![Sample {
1104 value: 3.0,
1105 timestamp: 1000,
1106 }]
1107 );
1108
1109 assert_eq!(
1110 vec![
1111 Label {
1112 name: METRIC_NAME_LABEL.to_string(),
1113 value: "metric1".to_string(),
1114 },
1115 Label {
1116 name: "instance".to_string(),
1117 value: "host2".to_string(),
1118 },
1119 ],
1120 timeseries[1].labels
1121 );
1122 assert_eq!(
1123 timeseries[1].samples,
1124 vec![Sample {
1125 value: 7.0,
1126 timestamp: 2000,
1127 }]
1128 );
1129 }
1130
1131 #[test]
1132 fn test_recordbatches_to_timeseries_borrows_and_groups_dictionary_labels() {
1133 let arrow_schema = Arc::new(ArrowSchema::new(vec![
1134 Field::new(
1135 greptime_timestamp(),
1136 ArrowDataType::Timestamp(arrow::datatypes::TimeUnit::Millisecond, None),
1137 false,
1138 ),
1139 Field::new(greptime_value(), ArrowDataType::Float64, false),
1140 Field::new_dictionary("instance", ArrowDataType::UInt32, ArrowDataType::Utf8, true),
1141 ]));
1142 let schema = Arc::new(Schema::try_from(arrow_schema.clone()).unwrap());
1143 let instance = DictionaryArray::<UInt32Type>::new(
1144 UInt32Array::from(vec![Some(0), None, Some(1), Some(0), Some(2)]),
1145 Arc::new(StringArray::from(vec![Some("host2"), Some("host1"), None])),
1146 );
1147 let batch = DfRecordBatch::try_new(
1148 arrow_schema,
1149 vec![
1150 Arc::new(TimestampMillisecondArray::from(vec![
1151 1000, 2000, 3000, 4000, 5000,
1152 ])),
1153 Arc::new(Float64Array::from(vec![1.0, 2.0, 3.0, 4.0, 5.0])),
1154 Arc::new(instance),
1155 ],
1156 )
1157 .unwrap();
1158 let recordbatch = RecordBatch::from_df_record_batch(schema.clone(), batch);
1159 let columns = label_columns(&recordbatch, greptime_timestamp(), greptime_value()).unwrap();
1160 assert!(matches!(
1161 columns[0].values,
1162 LabelValues::DictionaryUtf8 { .. }
1163 ));
1164 drop(columns);
1165 let recordbatches = RecordBatches::try_new(schema, vec![recordbatch]).unwrap();
1166
1167 let timeseries = recordbatches_to_timeseries(
1168 "metric1",
1169 greptime_timestamp(),
1170 greptime_value(),
1171 recordbatches,
1172 )
1173 .unwrap();
1174
1175 assert_eq!(3, timeseries.len());
1176 assert_eq!(
1177 vec![Label {
1178 name: METRIC_NAME_LABEL.to_string(),
1179 value: "metric1".to_string(),
1180 }],
1181 timeseries[0].labels
1182 );
1183 assert_eq!(
1184 vec![
1185 Sample {
1186 value: 2.0,
1187 timestamp: 2000,
1188 },
1189 Sample {
1190 value: 5.0,
1191 timestamp: 5000,
1192 },
1193 ],
1194 timeseries[0].samples
1195 );
1196 assert_eq!("host1", timeseries[1].labels[1].value);
1197 assert_eq!(
1198 vec![Sample {
1199 value: 3.0,
1200 timestamp: 3000,
1201 }],
1202 timeseries[1].samples
1203 );
1204 assert_eq!("host2", timeseries[2].labels[1].value);
1205 assert_eq!(
1206 vec![
1207 Sample {
1208 value: 1.0,
1209 timestamp: 1000,
1210 },
1211 Sample {
1212 value: 4.0,
1213 timestamp: 4000,
1214 },
1215 ],
1216 timeseries[2].samples
1217 );
1218 }
1219
1220 #[test]
1221 fn test_recordbatch_to_timeseries_groups_non_contiguous_series() {
1222 let schema = Arc::new(Schema::new(vec![
1223 ColumnSchema::new(
1224 greptime_timestamp(),
1225 ConcreteDataType::timestamp_millisecond_datatype(),
1226 true,
1227 ),
1228 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1229 ColumnSchema::new("instance", ConcreteDataType::string_datatype(), true),
1230 ]));
1231 let recordbatch = RecordBatch::new(
1232 schema,
1233 vec![
1234 Arc::new(TimestampMillisecondVector::from_vec(vec![1000, 2000, 3000])) as _,
1235 Arc::new(Float64Vector::from_vec(vec![1.0, 2.0, 3.0])) as _,
1236 Arc::new(StringVector::from(vec!["host2", "host1", "host2"])) as _,
1237 ],
1238 )
1239 .unwrap();
1240
1241 let timeseries = recordbatch_to_timeseries(
1242 "metric1",
1243 greptime_timestamp(),
1244 greptime_value(),
1245 recordbatch,
1246 )
1247 .unwrap();
1248
1249 assert_eq!("host1", timeseries[0].labels[1].value);
1251 assert_eq!("host2", timeseries[1].labels[1].value);
1252 assert_eq!(
1253 vec![
1254 Sample {
1255 value: 1.0,
1256 timestamp: 1000,
1257 },
1258 Sample {
1259 value: 3.0,
1260 timestamp: 3000,
1261 },
1262 ],
1263 timeseries[1].samples
1264 );
1265 }
1266
1267 #[test]
1268 fn test_recordbatch_to_timeseries_arrow_label_types_and_nulls() {
1269 let schema = Arc::new(Schema::new(vec![
1270 ColumnSchema::new(
1271 greptime_timestamp(),
1272 ConcreteDataType::timestamp_millisecond_datatype(),
1273 true,
1274 ),
1275 ColumnSchema::new(greptime_value(), ConcreteDataType::float64_datatype(), true),
1276 ColumnSchema::new("instance", ConcreteDataType::large_string_datatype(), true),
1277 ColumnSchema::new("zone", ConcreteDataType::utf8_view_datatype(), true),
1278 ColumnSchema::new("shard", ConcreteDataType::int32_datatype(), true),
1279 ]));
1280 let recordbatch = RecordBatch::new(
1281 schema,
1282 vec![
1283 Arc::new(TimestampMillisecondVector::from_vec(vec![1000, 2000, 3000])) as _,
1284 Arc::new(Float64Vector::from_vec(vec![1.0, 2.0, 3.0])) as _,
1285 Arc::new(StringVector::from(LargeStringArray::from(vec![
1286 "host2", "host1", "host2",
1287 ]))) as _,
1288 Arc::new(StringVector::from(StringViewArray::from(vec![
1289 Some("west"),
1290 None,
1291 Some("west"),
1292 ]))) as _,
1293 Arc::new(Int32Vector::from_vec(vec![2, 1, 2])) as _,
1294 ],
1295 )
1296 .unwrap();
1297
1298 let timeseries = recordbatch_to_timeseries(
1299 "metric1",
1300 greptime_timestamp(),
1301 greptime_value(),
1302 recordbatch,
1303 )
1304 .unwrap();
1305
1306 assert_eq!(2, timeseries.len());
1307 assert_eq!(
1308 vec![
1309 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
1310 new_label("instance".to_string(), "host1".to_string()),
1311 new_label("shard".to_string(), "1".to_string()),
1312 ],
1313 timeseries[0].labels
1314 );
1315 assert_eq!(
1316 vec![
1317 new_label(METRIC_NAME_LABEL.to_string(), "metric1".to_string()),
1318 new_label("instance".to_string(), "host2".to_string()),
1319 new_label("zone".to_string(), "west".to_string()),
1320 new_label("shard".to_string(), "2".to_string()),
1321 ],
1322 timeseries[1].labels
1323 );
1324 assert_eq!(
1325 vec![
1326 Sample {
1327 value: 1.0,
1328 timestamp: 1000,
1329 },
1330 Sample {
1331 value: 3.0,
1332 timestamp: 3000,
1333 },
1334 ],
1335 timeseries[1].samples
1336 );
1337 }
1338}