1use std::collections::{HashMap, HashSet};
16use std::sync::Arc;
17
18use async_trait::async_trait;
19use auth::{JAEGER_QUERY, PermissionReq, PermissionTableTarget, PermissionTableTargets};
20use catalog::CatalogManagerRef;
21use common_catalog::consts::{
22 TRACE_TABLE_NAME, trace_operations_table_name, trace_services_table_name,
23};
24use common_function::function::FunctionRef;
25use common_function::scalars::json::json_get::{
26 JsonGetBool, JsonGetFloat, JsonGetInt, JsonGetString, JsonGetWithType,
27};
28use common_function::scalars::udf::create_udf;
29use common_query::{Output, OutputData};
30use common_recordbatch::adapter::RecordBatchStreamAdapter;
31use common_recordbatch::util;
32use common_telemetry::warn;
33use datafusion::common::ScalarValue;
34use datafusion::dataframe::DataFrame;
35use datafusion::execution::SessionStateBuilder;
36use datafusion::execution::context::SessionContext;
37use datafusion::functions::core::expr_fn::coalesce;
38use datafusion::functions_window::expr_fn::row_number;
39use datafusion_expr::select_expr::SelectExpr;
40use datafusion_expr::{Expr, ExprFunctionExt, SortExpr, col, lit, lit_timestamp_nano, wildcard};
41use query::QueryEngineRef;
42use serde_json::Value as JsonValue;
43use servers::error::{
44 AuthSnafu, CollectRecordbatchSnafu, DataFusionSnafu, Result as ServerResult, TableNotFoundSnafu,
45};
46use servers::http::jaeger::{JAEGER_QUERY_TABLE_NAME_KEY, QueryTraceParams, TraceUserAgent};
47use servers::otlp::trace::{
48 DURATION_NANO_COLUMN, KEY_OTEL_STATUS_ERROR_KEY, RESOURCE_ATTRIBUTES_COLUMN,
49 SERVICE_NAME_COLUMN, SPAN_ATTRIBUTES_COLUMN, SPAN_KIND_COLUMN, SPAN_KIND_PREFIX,
50 SPAN_NAME_COLUMN, SPAN_STATUS_CODE, SPAN_STATUS_ERROR, TIMESTAMP_COLUMN, TRACE_ID_COLUMN,
51};
52use servers::query_handler::JaegerQueryHandler;
53use session::context::QueryContextRef;
54use snafu::{OptionExt, ResultExt};
55use table::TableRef;
56use table::table::adapter::DfTableProviderAdapter;
57
58use crate::instance::Instance;
59
60const DEFAULT_LIMIT: usize = 2000;
61const KEY_RN: &str = "greptime_rn";
62
63impl Instance {
64 async fn check_jaeger_query_permission(&self, ctx: &QueryContextRef) -> ServerResult<()> {
65 let table = ctx
66 .extension(JAEGER_QUERY_TABLE_NAME_KEY)
67 .unwrap_or(TRACE_TABLE_NAME);
68 let targets = PermissionTableTargets::resolved(vec![PermissionTableTarget::new(
69 ctx.current_catalog(),
70 ctx.current_schema(),
71 table,
72 )]);
73 let targets = self.resolve_query_permission_targets(targets, ctx).await?;
74 self.check_table_permission(ctx, PermissionReq::Action(JAEGER_QUERY), targets)
75 .context(AuthSnafu)?;
76 Ok(())
77 }
78}
79
80#[async_trait]
81impl JaegerQueryHandler for Instance {
82 async fn get_services(&self, ctx: QueryContextRef) -> ServerResult<Output> {
83 self.check_jaeger_query_permission(&ctx).await?;
84
85 Ok(query_trace_table(
87 ctx,
88 self,
89 vec![SelectExpr::from(col(SERVICE_NAME_COLUMN))],
90 vec![],
91 vec![],
92 None,
93 None,
94 vec![col(SERVICE_NAME_COLUMN)],
95 )
96 .await?)
97 }
98
99 async fn get_operations(
100 &self,
101 ctx: QueryContextRef,
102 service_name: &str,
103 span_kind: Option<&str>,
104 ) -> ServerResult<Output> {
105 self.check_jaeger_query_permission(&ctx).await?;
106
107 let mut filters = vec![col(SERVICE_NAME_COLUMN).eq(lit(service_name))];
108
109 if let Some(span_kind) = span_kind {
110 filters.push(col(SPAN_KIND_COLUMN).eq(lit(format!(
111 "{}{}",
112 SPAN_KIND_PREFIX,
113 span_kind.to_uppercase()
114 ))));
115 }
116
117 Ok(query_trace_table(
130 ctx,
131 self,
132 vec![
133 SelectExpr::from(col(SPAN_NAME_COLUMN)),
134 SelectExpr::from(col(SPAN_KIND_COLUMN)),
135 SelectExpr::from(col(SERVICE_NAME_COLUMN)),
136 SelectExpr::from(col(TIMESTAMP_COLUMN)),
137 ],
138 filters,
139 vec![col(SPAN_NAME_COLUMN).sort(true, false)], Some(DEFAULT_LIMIT),
141 None,
142 vec![col(SPAN_NAME_COLUMN), col(SPAN_KIND_COLUMN)],
143 )
144 .await?)
145 }
146
147 async fn get_trace(
148 &self,
149 ctx: QueryContextRef,
150 trace_id: &str,
151 start_time: Option<i64>,
152 end_time: Option<i64>,
153 limit: Option<usize>,
154 ) -> ServerResult<Output> {
155 self.check_jaeger_query_permission(&ctx).await?;
156
157 let selects = vec![wildcard()];
172
173 let mut filters = vec![col(TRACE_ID_COLUMN).eq(lit(trace_id))];
174
175 if let Some(start_time) = start_time {
176 filters.push(col(TIMESTAMP_COLUMN).gt_eq(lit_timestamp_nano(start_time)));
177 }
178
179 if let Some(end_time) = end_time {
180 filters.push(col(TIMESTAMP_COLUMN).lt_eq(lit_timestamp_nano(end_time)));
181 }
182
183 Ok(query_trace_table(
184 ctx,
185 self,
186 selects,
187 filters,
188 vec![col(TIMESTAMP_COLUMN).sort(false, false)], limit,
190 None,
191 vec![],
192 )
193 .await?)
194 }
195
196 async fn find_traces(
197 &self,
198 ctx: QueryContextRef,
199 query_params: QueryTraceParams,
200 ) -> ServerResult<Output> {
201 self.check_jaeger_query_permission(&ctx).await?;
202
203 let mut filters = vec![];
204
205 if let Some(service_name) = query_params.service_name {
206 filters.push(col(SERVICE_NAME_COLUMN).eq(lit(service_name)));
207 }
208
209 if let Some(operation_name) = query_params.operation_name {
210 filters.push(col(SPAN_NAME_COLUMN).eq(lit(operation_name)));
211 }
212
213 if let Some(start_time) = query_params.start_time {
214 filters.push(col(TIMESTAMP_COLUMN).gt_eq(lit_timestamp_nano(start_time)));
215 }
216
217 if let Some(end_time) = query_params.end_time {
218 filters.push(col(TIMESTAMP_COLUMN).lt_eq(lit_timestamp_nano(end_time)));
219 }
220
221 if let Some(min_duration) = query_params.min_duration {
222 filters.push(col(DURATION_NANO_COLUMN).gt_eq(lit(min_duration)));
223 }
224
225 if let Some(max_duration) = query_params.max_duration {
226 filters.push(col(DURATION_NANO_COLUMN).lt_eq(lit(max_duration)));
227 }
228
229 let output = query_trace_table(
246 ctx.clone(),
247 self,
248 vec![wildcard()],
249 filters,
250 vec![],
251 Some(query_params.limit.unwrap_or(DEFAULT_LIMIT)),
252 query_params.tags,
253 vec![col(TRACE_ID_COLUMN)],
254 )
255 .await?;
256
257 let mut filters = vec![
271 col(TRACE_ID_COLUMN).in_list(
272 trace_ids_from_output(output)
273 .await?
274 .iter()
275 .map(lit)
276 .collect::<Vec<Expr>>(),
277 false,
278 ),
279 ];
280
281 if !query_params.fetch_full_trace
282 && let Some(start_time) = query_params.start_time
283 {
284 filters.push(col(TIMESTAMP_COLUMN).gt_eq(lit_timestamp_nano(start_time)));
285 }
286
287 if !query_params.fetch_full_trace
288 && let Some(end_time) = query_params.end_time
289 {
290 filters.push(col(TIMESTAMP_COLUMN).lt_eq(lit_timestamp_nano(end_time)));
291 }
292
293 match query_params.user_agent {
294 TraceUserAgent::Grafana => {
295 let table_name = ctx
299 .extension(JAEGER_QUERY_TABLE_NAME_KEY)
300 .unwrap_or(TRACE_TABLE_NAME);
301
302 let table = get_table(ctx.clone(), self.catalog_manager(), table_name).await?;
303
304 Ok(find_traces_rank_3(
305 table,
306 self.query_engine(),
307 filters,
308 vec![col(TIMESTAMP_COLUMN).sort(false, false)], )
310 .await?)
311 }
312 _ => {
313 Ok(query_trace_table(
315 ctx,
316 self,
317 vec![wildcard()],
318 filters,
319 vec![col(TIMESTAMP_COLUMN).sort(false, false)], None,
321 None,
322 vec![],
323 )
324 .await?)
325 }
326 }
327 }
328}
329
330#[allow(clippy::too_many_arguments)]
331async fn query_trace_table(
332 ctx: QueryContextRef,
333 instance: &Instance,
334 selects: Vec<SelectExpr>,
335 filters: Vec<Expr>,
336 sorts: Vec<SortExpr>,
337 limit: Option<usize>,
338 tags: Option<HashMap<String, JsonValue>>,
339 distincts: Vec<Expr>,
340) -> ServerResult<Output> {
341 let trace_table_name = ctx
342 .extension(JAEGER_QUERY_TABLE_NAME_KEY)
343 .unwrap_or(TRACE_TABLE_NAME);
344
345 let table_name = {
348 if match selects.as_slice() {
349 [SelectExpr::Expression(x)] => x == &col(SERVICE_NAME_COLUMN),
350 _ => false,
351 } {
352 &trace_services_table_name(trace_table_name)
353 } else if !distincts.is_empty()
354 && distincts.contains(&col(SPAN_NAME_COLUMN))
355 && distincts.contains(&col(SPAN_KIND_COLUMN))
356 {
357 &trace_operations_table_name(trace_table_name)
358 } else {
359 trace_table_name
360 }
361 };
362
363 let table = instance
364 .catalog_manager()
365 .table(
366 ctx.current_catalog(),
367 &ctx.current_schema(),
368 table_name,
369 Some(&ctx),
370 )
371 .await?
372 .with_context(|| TableNotFoundSnafu {
373 table: table_name,
374 catalog: ctx.current_catalog(),
375 schema: ctx.current_schema(),
376 })?;
377
378 let table_info = table.table_info();
379 let data_model = table_info.meta.options.data_model();
380
381 let col_names = table_info
383 .meta
384 .field_column_names()
385 .map(|s| format!("\"{}\"", s))
386 .collect::<HashSet<String>>();
387
388 let df_context = create_df_context(instance.query_engine())?;
389
390 let dataframe = df_context
391 .read_table(Arc::new(DfTableProviderAdapter::new(table)))
392 .context(DataFusionSnafu)?;
393
394 let dataframe = dataframe.select(selects).context(DataFusionSnafu)?;
395
396 let dataframe = filters
398 .into_iter()
399 .chain(tags.map_or(Ok(vec![]), |t| {
400 tags_filters(&dataframe, t, data_model, &col_names)
401 })?)
402 .try_fold(dataframe, |df, expr| {
403 df.filter(expr).context(DataFusionSnafu)
404 })?;
405
406 let dataframe = if !distincts.is_empty() {
408 dataframe
409 .distinct_on(distincts.clone(), distincts, None)
410 .context(DataFusionSnafu)?
411 } else {
412 dataframe
413 };
414
415 let dataframe = if !sorts.is_empty() {
417 dataframe.sort(sorts).context(DataFusionSnafu)?
418 } else {
419 dataframe
420 };
421
422 let dataframe = if let Some(limit) = limit {
424 dataframe.limit(0, Some(limit)).context(DataFusionSnafu)?
425 } else {
426 dataframe
427 };
428
429 let stream = dataframe.execute_stream().await.context(DataFusionSnafu)?;
431
432 let output = Output::new_with_stream(Box::pin(
433 RecordBatchStreamAdapter::try_new(stream).context(CollectRecordbatchSnafu)?,
434 ));
435
436 output
437 .map_dictionary_to_values()
438 .context(CollectRecordbatchSnafu)
439}
440
441async fn get_table(
442 ctx: QueryContextRef,
443 catalog_manager: &CatalogManagerRef,
444 table_name: &str,
445) -> ServerResult<TableRef> {
446 catalog_manager
447 .table(
448 ctx.current_catalog(),
449 &ctx.current_schema(),
450 table_name,
451 Some(&ctx),
452 )
453 .await?
454 .with_context(|| TableNotFoundSnafu {
455 table: table_name,
456 catalog: ctx.current_catalog(),
457 schema: ctx.current_schema(),
458 })
459}
460
461async fn find_traces_rank_3(
462 table: TableRef,
463 query_engine: &QueryEngineRef,
464 filters: Vec<Expr>,
465 sorts: Vec<SortExpr>,
466) -> ServerResult<Output> {
467 let df_context = create_df_context(query_engine)?;
468
469 let dataframe = df_context
470 .read_table(Arc::new(DfTableProviderAdapter::new(table)))
471 .context(DataFusionSnafu)?;
472
473 let dataframe = dataframe
474 .select(vec![wildcard()])
475 .context(DataFusionSnafu)?;
476
477 let dataframe = filters.into_iter().try_fold(dataframe, |df, expr| {
479 df.filter(expr).context(DataFusionSnafu)
480 })?;
481
482 let dataframe = if !sorts.is_empty() {
484 dataframe.sort(sorts).context(DataFusionSnafu)?
485 } else {
486 dataframe
487 };
488
489 let trace_id_col = vec![col(TRACE_ID_COLUMN)];
491 let timestamp_asc = vec![col(TIMESTAMP_COLUMN).sort(true, false)];
492
493 let dataframe = dataframe
494 .with_column(
495 KEY_RN,
496 row_number()
497 .partition_by(trace_id_col)
498 .order_by(timestamp_asc)
499 .build()
500 .context(DataFusionSnafu)?,
501 )
502 .context(DataFusionSnafu)?;
503
504 let dataframe = dataframe
505 .filter(col(KEY_RN).lt_eq(lit(3)))
506 .context(DataFusionSnafu)?;
507
508 let stream = dataframe.execute_stream().await.context(DataFusionSnafu)?;
510
511 let output = Output::new_with_stream(Box::pin(
512 RecordBatchStreamAdapter::try_new(stream).context(CollectRecordbatchSnafu)?,
513 ));
514
515 output
516 .map_dictionary_to_values()
517 .context(CollectRecordbatchSnafu)
518}
519
520fn create_df_context(query_engine: &QueryEngineRef) -> ServerResult<SessionContext> {
525 let df_context = SessionContext::new_with_state(
526 SessionStateBuilder::new_from_existing(query_engine.engine_state().session_state()).build(),
527 );
528
529 let udfs: Vec<FunctionRef> = vec![
531 Arc::new(JsonGetWithType::default()),
532 Arc::new(JsonGetInt::default()),
533 Arc::new(JsonGetFloat::default()),
534 Arc::new(JsonGetBool::default()),
535 Arc::new(JsonGetString::default()),
536 ];
537
538 for udf in udfs {
539 df_context.register_udf(create_udf(udf));
540 }
541
542 Ok(df_context)
543}
544
545fn json_tag_filters(
546 dataframe: &DataFrame,
547 tags: HashMap<String, JsonValue>,
548) -> ServerResult<Vec<Expr>> {
549 let has_resource_attributes = dataframe
550 .schema()
551 .has_column_with_unqualified_name(RESOURCE_ATTRIBUTES_COLUMN);
552 let mut filters = vec![];
553
554 for (key, value) in tags.iter() {
556 let (udf, value) = match value {
557 JsonValue::String(value) => (JsonGetString::NAME, lit(value)),
558 JsonValue::Number(value) if value.is_i64() => {
559 (JsonGetInt::NAME, lit(value.as_i64().unwrap()))
560 }
561 JsonValue::Number(value) if value.is_f64() => {
562 (JsonGetFloat::NAME, lit(value.as_f64().unwrap()))
563 }
564 JsonValue::Bool(value) => (JsonGetBool::NAME, lit(*value)),
565 _ => continue,
566 };
567 let get = dataframe.registry().udf(udf).context(DataFusionSnafu)?;
568 let path = lit(format!("[\"{}\"]", key));
569 let span_value = get.call(vec![col(SPAN_ATTRIBUTES_COLUMN), path.clone()]);
570 let attribute = if has_resource_attributes {
572 coalesce(vec![
573 span_value,
574 get.call(vec![col(RESOURCE_ATTRIBUTES_COLUMN), path]),
575 ])
576 } else {
577 span_value
578 };
579 filters.push(attribute.eq(value));
580 }
581
582 Ok(filters)
583}
584
585fn json2_tag_filters(
587 dataframe: &DataFrame,
588 tags: HashMap<String, JsonValue>,
589) -> ServerResult<Vec<Expr>> {
590 let get = dataframe
591 .registry()
592 .udf(JsonGetWithType::NAME)
593 .context(DataFusionSnafu)?;
594 tags.into_iter()
595 .map(|(key, value)| {
596 if key == KEY_OTEL_STATUS_ERROR_KEY && value == JsonValue::Bool(true) {
597 return Ok(col(SPAN_STATUS_CODE).eq(lit(SPAN_STATUS_ERROR)));
598 }
599 let path = lit(format!("$[{}]", JsonValue::String(key)));
601 let (value, value_type) = match value {
602 JsonValue::String(value) => (
603 ScalarValue::Utf8View(Some(value)),
604 ScalarValue::Utf8View(None),
605 ),
606 JsonValue::Bool(value) => (
607 ScalarValue::Boolean(Some(value)),
608 ScalarValue::Boolean(None),
609 ),
610 JsonValue::Number(value) => {
611 if let Some(value) = value.as_i64() {
612 (ScalarValue::Int64(Some(value)), ScalarValue::Int64(None))
613 } else if let Some(value) = value.as_u64() {
614 (ScalarValue::UInt64(Some(value)), ScalarValue::UInt64(None))
615 } else if let Some(value) = value.as_f64() {
616 (
617 ScalarValue::Float64(Some(value)),
618 ScalarValue::Float64(None),
619 )
620 } else {
621 return Ok(lit(false));
622 }
623 }
624 JsonValue::Null => (ScalarValue::Utf8View(None), ScalarValue::Utf8View(None)),
625 JsonValue::Array(_) | JsonValue::Object(_) => return Ok(lit(false)),
626 };
627 let attribute = coalesce(vec![
628 get.call(vec![
629 col(SPAN_ATTRIBUTES_COLUMN),
630 path.clone(),
631 lit(value_type.clone()),
632 ]),
633 get.call(vec![col(RESOURCE_ATTRIBUTES_COLUMN), path, lit(value_type)]),
634 ]);
635 Ok(if value.is_null() {
636 attribute.is_null()
637 } else {
638 attribute.eq(lit(value))
639 })
640 })
641 .collect()
642}
643
644#[inline]
647fn check_col_and_build_expr<F>(
648 span_key: String,
649 resource_key: String,
650 key: &str,
651 col_names: &HashSet<String>,
652 expr_builder: F,
653) -> Option<Expr>
654where
655 F: FnOnce(String) -> Expr,
656{
657 if col_names.contains(&span_key) {
658 return Some(expr_builder(span_key));
659 }
660 if col_names.contains(&resource_key) {
661 return Some(expr_builder(resource_key));
662 }
663 warn!("tag key {} not found in table columns", key);
664 None
665}
666
667fn flatten_tag_filters(
668 tags: HashMap<String, JsonValue>,
669 col_names: &HashSet<String>,
670) -> ServerResult<Vec<Expr>> {
671 let filters = tags
672 .into_iter()
673 .filter_map(|(key, value)| {
674 if key == KEY_OTEL_STATUS_ERROR_KEY && value == JsonValue::Bool(true) {
675 return Some(col(SPAN_STATUS_CODE).eq(lit(SPAN_STATUS_ERROR)));
676 }
677
678 let span_key = format!("\"span_attributes.{}\"", key);
680 let resource_key = format!("\"resource_attributes.{}\"", key);
681 match value {
682 JsonValue::String(value) => {
683 check_col_and_build_expr(span_key, resource_key, &key, col_names, |k| {
684 col(k).eq(lit(value))
685 })
686 }
687 JsonValue::Number(value) => {
688 if value.is_f64() {
689 let value = value.as_f64().unwrap();
691 check_col_and_build_expr(span_key, resource_key, &key, col_names, |k| {
692 col(k).eq(lit(value))
693 })
694 } else {
695 let value = value.as_i64().unwrap();
696 check_col_and_build_expr(span_key, resource_key, &key, col_names, |k| {
697 col(k).eq(lit(value))
698 })
699 }
700 }
701 JsonValue::Bool(value) => {
702 check_col_and_build_expr(span_key, resource_key, &key, col_names, |k| {
703 col(k).eq(lit(value))
704 })
705 }
706 JsonValue::Null => {
707 check_col_and_build_expr(span_key, resource_key, &key, col_names, |k| {
708 col(k).is_null()
709 })
710 }
711 JsonValue::Array(_value) => None,
713 JsonValue::Object(_value) => None,
714 }
715 })
716 .collect();
717 Ok(filters)
718}
719
720fn tags_filters(
721 dataframe: &DataFrame,
722 tags: HashMap<String, JsonValue>,
723 data_model: Option<&str>,
724 col_names: &HashSet<String>,
725) -> ServerResult<Vec<Expr>> {
726 match data_model {
727 Some(table::requests::TABLE_DATA_MODEL_TRACE_V1) => flatten_tag_filters(tags, col_names),
728 Some(table::requests::TABLE_DATA_MODEL_TRACE_V2) => json2_tag_filters(dataframe, tags),
729 _ => json_tag_filters(dataframe, tags),
730 }
731}
732
733async fn trace_ids_from_output(output: Output) -> ServerResult<Vec<String>> {
735 if let OutputData::Stream(stream) = output.data {
736 let schema = stream.schema().clone();
737 let recordbatches = util::collect(stream)
738 .await
739 .context(CollectRecordbatchSnafu)?;
740
741 if !recordbatches.is_empty()
743 && schema.num_columns() == 1
744 && schema.contains_column(TRACE_ID_COLUMN)
745 {
746 let mut trace_ids = vec![];
747 for recordbatch in recordbatches {
748 recordbatch
749 .iter_column_as_string(0)
750 .flatten()
751 .for_each(|x| trace_ids.push(x));
752 }
753
754 return Ok(trace_ids);
755 }
756 }
757
758 Ok(vec![])
759}