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