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