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