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