1use std::fmt::{self, Display};
16use std::future::Future;
17use std::marker::PhantomData;
18use std::pin::Pin;
19use std::str::FromStr;
20use std::sync::Arc;
21use std::sync::atomic::{AtomicU64, Ordering};
22use std::task::{Context, Poll};
23
24use common_base::readable_size::ReadableSize;
25use common_telemetry::tracing::{Span, info_span};
26use common_time::util::format_nanoseconds_human_readable;
27use datafusion::arrow::compute::cast;
28use datafusion::arrow::datatypes::SchemaRef as DfSchemaRef;
29use datafusion::error::Result as DfResult;
30use datafusion::execution::context::ExecutionProps;
31use datafusion::logical_expr::Expr;
32use datafusion::logical_expr::utils::conjunction;
33use datafusion::physical_expr::create_physical_expr;
34use datafusion::physical_plan::metrics::{BaselineMetrics, MetricValue};
35use datafusion::physical_plan::{
36 DisplayFormatType, ExecutionPlan, ExecutionPlanVisitor, PhysicalExpr,
37 RecordBatchStream as DfRecordBatchStream, accept,
38};
39use datafusion_common::arrow::error::ArrowError;
40use datafusion_common::{DataFusionError, ToDFSchema};
41use datatypes::arrow::array::Array;
42use datatypes::arrow::datatypes::DataType as ArrowDataType;
43use datatypes::schema::{ColumnExtType, Schema, SchemaRef};
44use futures::ready;
45use jsonb;
46use pin_project::pin_project;
47use snafu::ResultExt;
48
49use crate::error::{self, Result};
50use crate::filter::batch_filter;
51use crate::{
52 DfRecordBatch, DfSendableRecordBatchStream, OrderOption, RecordBatch, RecordBatchStream,
53 SendableRecordBatchStream, Stream,
54};
55
56const REGION_SCAN_EXEC_NAME: &str = "RegionScanExec";
57
58type FutureStream =
59 Pin<Box<dyn std::future::Future<Output = Result<SendableRecordBatchStream>> + Send>>;
60
61#[pin_project]
63pub struct RecordBatchStreamTypeAdapter<T, E> {
64 #[pin]
65 stream: T,
66 projected_schema: DfSchemaRef,
67 projection: Vec<usize>,
68 predicate: Option<Arc<dyn PhysicalExpr>>,
69 phantom: PhantomData<E>,
70}
71
72impl<T, E> RecordBatchStreamTypeAdapter<T, E>
73where
74 T: Stream<Item = std::result::Result<DfRecordBatch, E>>,
75 E: std::error::Error + Send + Sync + 'static,
76{
77 pub fn new(projected_schema: DfSchemaRef, stream: T, projection: Option<Vec<usize>>) -> Self {
78 let projection = if let Some(projection) = projection {
79 projection
80 } else {
81 (0..projected_schema.fields().len()).collect()
82 };
83
84 Self {
85 stream,
86 projected_schema,
87 projection,
88 predicate: None,
89 phantom: Default::default(),
90 }
91 }
92
93 pub fn with_filter(mut self, filters: Vec<Expr>) -> Result<Self> {
94 let filters = if let Some(expr) = conjunction(filters) {
95 let df_schema = self
96 .projected_schema
97 .clone()
98 .to_dfschema_ref()
99 .context(error::PhysicalExprSnafu)?;
100
101 let filters = create_physical_expr(&expr, &df_schema, &ExecutionProps::new())
102 .context(error::PhysicalExprSnafu)?;
103 Some(filters)
104 } else {
105 None
106 };
107 self.predicate = filters;
108 Ok(self)
109 }
110}
111
112impl<T, E> DfRecordBatchStream for RecordBatchStreamTypeAdapter<T, E>
113where
114 T: Stream<Item = std::result::Result<DfRecordBatch, E>>,
115 E: std::error::Error + Send + Sync + 'static,
116{
117 fn schema(&self) -> DfSchemaRef {
118 self.projected_schema.clone()
119 }
120}
121
122impl<T, E> Stream for RecordBatchStreamTypeAdapter<T, E>
123where
124 T: Stream<Item = std::result::Result<DfRecordBatch, E>>,
125 E: std::error::Error + Send + Sync + 'static,
126{
127 type Item = DfResult<DfRecordBatch>;
128
129 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
130 let this = self.project();
131
132 let batch = futures::ready!(this.stream.poll_next(cx))
133 .map(|r| r.map_err(|e| DataFusionError::External(Box::new(e))));
134
135 let projected_schema = this.projected_schema.clone();
136 let projection = this.projection.clone();
137 let predicate = this.predicate.clone();
138
139 let batch = batch.map(|b| {
140 b.and_then(|b| {
141 let projected_column = b.project(&projection)?;
142 if projected_column.schema().fields.len() != projected_schema.fields.len() {
143 return Err(DataFusionError::ArrowError(Box::new(ArrowError::SchemaError(format!(
144 "Trying to cast a RecordBatch into an incompatible schema. RecordBatch: {}, Target: {}",
145 projected_column.schema(),
146 projected_schema,
147 ))), None));
148 }
149
150 let mut columns = Vec::with_capacity(projected_schema.fields.len());
151 for (idx,field) in projected_schema.fields.iter().enumerate() {
152 let column = projected_column.column(idx);
153 let extype = field.metadata().get("greptime:type").and_then(|s| ColumnExtType::from_str(s).ok());
154 let output = custom_cast(&column, field.data_type(), extype)?;
155 columns.push(output)
156 }
157 let record_batch = DfRecordBatch::try_new(projected_schema, columns)?;
158 let record_batch = if let Some(predicate) = predicate {
159 batch_filter(&record_batch, &predicate)?
160 } else {
161 record_batch
162 };
163 Ok(record_batch)
164 })
165 });
166
167 Poll::Ready(batch)
168 }
169
170 #[inline]
171 fn size_hint(&self) -> (usize, Option<usize>) {
172 self.stream.size_hint()
173 }
174}
175
176pub struct DfRecordBatchStreamAdapter {
179 stream: SendableRecordBatchStream,
180}
181
182impl DfRecordBatchStreamAdapter {
183 pub fn new(stream: SendableRecordBatchStream) -> Self {
184 Self { stream }
185 }
186}
187
188impl DfRecordBatchStream for DfRecordBatchStreamAdapter {
189 fn schema(&self) -> DfSchemaRef {
190 self.stream.schema().arrow_schema().clone()
191 }
192}
193
194impl Stream for DfRecordBatchStreamAdapter {
195 type Item = DfResult<DfRecordBatch>;
196
197 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
198 match Pin::new(&mut self.stream).poll_next(cx) {
199 Poll::Pending => Poll::Pending,
200 Poll::Ready(Some(recordbatch)) => match recordbatch {
201 Ok(recordbatch) => Poll::Ready(Some(Ok(recordbatch.into_df_record_batch()))),
202 Err(e) => Poll::Ready(Some(Err(DataFusionError::External(Box::new(e))))),
203 },
204 Poll::Ready(None) => Poll::Ready(None),
205 }
206 }
207
208 #[inline]
209 fn size_hint(&self) -> (usize, Option<usize>) {
210 self.stream.size_hint()
211 }
212}
213
214pub struct RecordBatchStreamAdapter {
218 schema: SchemaRef,
219 stream: DfSendableRecordBatchStream,
220 metrics: Option<BaselineMetrics>,
221 metrics_2: Metrics,
223 query_load_region_id: Option<u64>,
224 query_stat_counters: Option<RegionQueryStatCounters>,
225 explain_verbose: bool,
227 span: Span,
228}
229
230#[derive(Debug, Clone)]
232pub struct RegionQueryStatCounters {
233 pub query_cpu_time: Arc<AtomicU64>,
235 pub query_scanned_bytes: Arc<AtomicU64>,
237}
238
239enum Metrics {
241 Unavailable,
242 Unresolved(Arc<dyn ExecutionPlan>),
243 PartialResolved(Arc<dyn ExecutionPlan>, RecordBatchMetrics),
244 Resolved(RecordBatchMetrics),
245}
246
247impl RecordBatchStreamAdapter {
248 pub fn try_new(stream: DfSendableRecordBatchStream) -> Result<Self> {
249 let schema =
250 Arc::new(Schema::try_from(stream.schema()).context(error::SchemaConversionSnafu)?);
251 Ok(Self {
252 schema,
253 stream,
254 metrics: None,
255 metrics_2: Metrics::Unavailable,
256 query_load_region_id: None,
257 query_stat_counters: None,
258 explain_verbose: false,
259 span: Span::current(),
260 })
261 }
262
263 pub fn try_new_with_span(stream: DfSendableRecordBatchStream, span: Span) -> Result<Self> {
264 let schema =
265 Arc::new(Schema::try_from(stream.schema()).context(error::SchemaConversionSnafu)?);
266 let subspan = info_span!(parent: &span, "RecordBatchStreamAdapter");
267 Ok(Self {
268 schema,
269 stream,
270 metrics: None,
271 metrics_2: Metrics::Unavailable,
272 query_load_region_id: None,
273 query_stat_counters: None,
274 explain_verbose: false,
275 span: subspan,
276 })
277 }
278
279 pub fn set_metrics2(&mut self, plan: Arc<dyn ExecutionPlan>) {
280 self.metrics_2 = Metrics::Unresolved(plan)
281 }
282
283 fn record_query_stats_on_drop(&self) {
284 let Some(counters) = &self.query_stat_counters else {
285 return;
286 };
287
288 match &self.metrics_2 {
289 Metrics::Unresolved(df_plan) => {
290 let metrics = collect_lightweight_query_load_metrics(
291 df_plan.as_ref(),
292 self.query_load_region_id,
293 );
294 record_query_stats(counters, &metrics);
295 }
296 Metrics::PartialResolved(_, metrics) | Metrics::Resolved(metrics) => {
297 record_query_stats(counters, metrics);
298 }
299 Metrics::Unavailable => {}
300 }
301 }
302
303 pub fn set_query_load_region_id(&mut self, region_id: Option<u64>) {
304 self.query_load_region_id = region_id;
305 }
306
307 pub fn set_query_stat_counters(&mut self, counters: Option<RegionQueryStatCounters>) {
308 self.query_stat_counters = counters;
309 }
310
311 pub fn set_explain_verbose(&mut self, verbose: bool) {
313 self.explain_verbose = verbose;
314 }
315
316 fn collect_plan_metrics(&self, df_plan: &Arc<dyn ExecutionPlan>) -> RecordBatchMetrics {
317 collect_full_metrics(
318 df_plan.as_ref(),
319 self.explain_verbose,
320 self.query_load_region_id,
321 )
322 }
323
324 fn collect_partial_metrics(
325 df_plan: &dyn ExecutionPlan,
326 explain_verbose: bool,
327 query_load_region_id: Option<u64>,
328 ) -> RecordBatchMetrics {
329 if explain_verbose {
330 collect_full_metrics(df_plan, explain_verbose, query_load_region_id)
331 } else {
332 collect_lightweight_query_load_metrics(df_plan, query_load_region_id)
333 }
334 }
335
336 fn update_plan_metrics(&mut self, final_metrics: bool) {
337 if final_metrics {
338 let df_plan = match &self.metrics_2 {
339 Metrics::Unresolved(df_plan) | Metrics::PartialResolved(df_plan, _) => {
340 df_plan.clone()
341 }
342 Metrics::Unavailable | Metrics::Resolved(_) => return,
343 };
344 let metrics = self.collect_plan_metrics(&df_plan);
345 self.metrics_2 = Metrics::Resolved(metrics);
346 } else {
347 let explain_verbose = self.explain_verbose;
348 let query_load_region_id = self.query_load_region_id;
349 match &mut self.metrics_2 {
350 Metrics::Unresolved(df_plan) => {
351 let df_plan = df_plan.clone();
352 let metrics = Self::collect_partial_metrics(
353 df_plan.as_ref(),
354 explain_verbose,
355 query_load_region_id,
356 );
357 self.metrics_2 = Metrics::PartialResolved(df_plan, metrics);
358 }
359 Metrics::PartialResolved(df_plan, metrics) => {
360 *metrics = Self::collect_partial_metrics(
361 df_plan.as_ref(),
362 explain_verbose,
363 query_load_region_id,
364 );
365 }
366 Metrics::Unavailable | Metrics::Resolved(_) => {}
367 }
368 }
369 }
370}
371
372pub fn region_scan_output_bytes(metrics: &RecordBatchMetrics) -> usize {
374 metrics
375 .plan_metrics
376 .iter()
377 .filter(|pm| pm.plan_name == REGION_SCAN_EXEC_NAME)
378 .flat_map(|pm| &pm.metrics)
379 .filter_map(|(name, value)| (name == "output_bytes").then_some(*value))
380 .sum()
381}
382
383fn record_query_stats(counters: &RegionQueryStatCounters, metrics: &RecordBatchMetrics) {
384 counters
385 .query_cpu_time
386 .fetch_add(metrics.elapsed_compute as u64, Ordering::Relaxed);
387 counters
388 .query_scanned_bytes
389 .fetch_add(region_scan_output_bytes(metrics) as u64, Ordering::Relaxed);
390}
391
392fn collect_full_metrics(
394 df_plan: &dyn ExecutionPlan,
395 explain_verbose: bool,
396 query_load_region_id: Option<u64>,
397) -> RecordBatchMetrics {
398 let mut metric_collector = MetricCollector::new(explain_verbose);
399 accept(df_plan, &mut metric_collector).unwrap();
400 metric_collector.record_batch_metrics.query_load_region_id = query_load_region_id;
401 metric_collector.record_batch_metrics
402}
403
404fn collect_lightweight_query_load_metrics(
411 df_plan: &dyn ExecutionPlan,
412 query_load_region_id: Option<u64>,
413) -> RecordBatchMetrics {
414 let mut metrics = RecordBatchMetrics {
415 query_load_region_id,
416 ..Default::default()
417 };
418 collect_lightweight_query_load_metrics_inner(df_plan, 0, &mut metrics);
419 metrics
420}
421
422fn collect_lightweight_query_load_metrics_inner(
425 df_plan: &dyn ExecutionPlan,
426 level: usize,
427 record_batch_metrics: &mut RecordBatchMetrics,
428) {
429 let is_region_scan = df_plan.name() == REGION_SCAN_EXEC_NAME;
430 let mut region_scan_output_bytes = None;
431
432 if let Some(metrics) = df_plan.metrics() {
433 for metric in metrics.iter() {
434 let value = metric.value();
435 match value {
436 MetricValue::ElapsedCompute(elapsed_compute) => {
437 record_batch_metrics.elapsed_compute += elapsed_compute.value();
438 }
439 MetricValue::CurrentMemoryUsage(memory_usage) => {
440 record_batch_metrics.memory_usage += memory_usage.value();
441 }
442 _ => {}
443 }
444
445 if is_region_scan && value.name() == "output_bytes" {
446 *region_scan_output_bytes.get_or_insert(0) += value.as_usize();
447 }
448 }
449 }
450
451 if let Some(output_bytes) = region_scan_output_bytes {
452 record_batch_metrics.plan_metrics.push(PlanMetrics {
453 plan: df_plan.name().to_string(),
454 plan_name: df_plan.name().to_string(),
455 level,
456 metrics: vec![("output_bytes".to_string(), output_bytes)],
457 });
458 }
459
460 for child in df_plan.children() {
461 collect_lightweight_query_load_metrics_inner(
462 child.as_ref(),
463 level + 1,
464 record_batch_metrics,
465 );
466 }
467}
468
469impl RecordBatchStream for RecordBatchStreamAdapter {
470 fn name(&self) -> &str {
471 "RecordBatchStreamAdapter"
472 }
473
474 fn schema(&self) -> SchemaRef {
475 self.schema.clone()
476 }
477
478 fn metrics(&self) -> Option<RecordBatchMetrics> {
479 match &self.metrics_2 {
480 Metrics::Unresolved(df_plan) => {
481 if self.explain_verbose {
482 Some(self.collect_plan_metrics(df_plan))
483 } else {
484 None
485 }
486 }
487 Metrics::PartialResolved(df_plan, metrics) => Some(if self.explain_verbose {
488 self.collect_plan_metrics(df_plan)
489 } else {
490 metrics.clone()
491 }),
492 Metrics::Resolved(metrics) => Some(metrics.clone()),
493 Metrics::Unavailable => None,
494 }
495 }
496
497 fn output_ordering(&self) -> Option<&[OrderOption]> {
498 None
499 }
500}
501
502impl Stream for RecordBatchStreamAdapter {
503 type Item = Result<RecordBatch>;
504
505 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
506 let timer = self
507 .metrics
508 .as_ref()
509 .map(|m| m.elapsed_compute().clone())
510 .unwrap_or_default();
511 let _guard = timer.timer();
512 let poll_span = info_span!(parent: &self.span, "poll_next");
513 let _entered = poll_span.enter();
514 match Pin::new(&mut self.stream).poll_next(cx) {
515 Poll::Pending => Poll::Pending,
516 Poll::Ready(Some(df_record_batch)) => {
517 let df_record_batch = df_record_batch?;
518 self.update_plan_metrics(false);
519 Poll::Ready(Some(Ok(RecordBatch::from_df_record_batch(
520 self.schema(),
521 df_record_batch,
522 ))))
523 }
524 Poll::Ready(None) => {
525 self.update_plan_metrics(true);
526 Poll::Ready(None)
527 }
528 }
529 }
530
531 #[inline]
532 fn size_hint(&self) -> (usize, Option<usize>) {
533 self.stream.size_hint()
534 }
535}
536
537impl Drop for RecordBatchStreamAdapter {
538 fn drop(&mut self) {
539 self.record_query_stats_on_drop();
540 }
541}
542
543pub struct MetricCollector {
545 current_level: usize,
546 pub record_batch_metrics: RecordBatchMetrics,
547 verbose: bool,
548}
549
550impl MetricCollector {
551 pub fn new(verbose: bool) -> Self {
552 Self {
553 current_level: 0,
554 record_batch_metrics: RecordBatchMetrics::default(),
555 verbose,
556 }
557 }
558}
559
560impl ExecutionPlanVisitor for MetricCollector {
561 type Error = !;
562
563 fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> std::result::Result<bool, Self::Error> {
564 let Some(metric) = plan.metrics() else {
566 self.record_batch_metrics.plan_metrics.push(PlanMetrics {
567 plan: plan.name().to_string(),
568 plan_name: plan.name().to_string(),
569 level: self.current_level,
570 metrics: vec![],
571 });
572 self.current_level += 1;
573 return Ok(true);
574 };
575
576 let metric = metric
578 .aggregate_by_name()
579 .sorted_for_display()
580 .timestamps_removed();
581 let mut plan_metric = PlanMetrics {
582 plan: one_line(plan, self.verbose).to_string(),
583 plan_name: plan.name().to_string(),
584 level: self.current_level,
585 metrics: Vec::with_capacity(metric.iter().size_hint().0),
586 };
587 for m in metric.iter() {
588 plan_metric
589 .metrics
590 .push((m.value().name().to_string(), m.value().as_usize()));
591
592 match m.value() {
594 MetricValue::ElapsedCompute(ec) => {
595 self.record_batch_metrics.elapsed_compute += ec.value()
596 }
597 MetricValue::CurrentMemoryUsage(m) => {
598 self.record_batch_metrics.memory_usage += m.value()
599 }
600 _ => {}
601 }
602 }
603 self.record_batch_metrics.plan_metrics.push(plan_metric);
604
605 self.current_level += 1;
606 Ok(true)
607 }
608
609 fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> std::result::Result<bool, Self::Error> {
610 self.current_level -= 1;
611 Ok(true)
612 }
613}
614
615fn one_line(plan: &dyn ExecutionPlan, verbose: bool) -> impl fmt::Display + '_ {
618 struct Wrapper<'a> {
619 plan: &'a dyn ExecutionPlan,
620 format_type: DisplayFormatType,
621 }
622
623 impl fmt::Display for Wrapper<'_> {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 self.plan.fmt_as(self.format_type, f)?;
626 writeln!(f)
627 }
628 }
629
630 let format_type = if verbose {
631 DisplayFormatType::Verbose
632 } else {
633 DisplayFormatType::Default
634 };
635 Wrapper { plan, format_type }
636}
637
638#[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)]
641pub struct RecordBatchMetrics {
642 pub elapsed_compute: usize,
645 pub memory_usage: usize,
647 pub plan_metrics: Vec<PlanMetrics>,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
652 pub query_load_region_id: Option<u64>,
653 #[serde(default, skip_serializing_if = "Vec::is_empty")]
666 pub region_watermarks: Vec<RegionWatermarkEntry>,
667}
668
669#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
670pub struct RegionWatermarkEntry {
671 pub region_id: u64,
672 #[serde(default, skip_serializing_if = "Option::is_none")]
673 pub watermark: Option<u64>,
674}
675
676fn is_time_metric(metric_name: &str) -> bool {
678 metric_name.contains("elapsed") || metric_name.contains("time") || metric_name.contains("cost")
679}
680
681fn is_bytes_metric(metric_name: &str) -> bool {
683 metric_name.contains("bytes") || metric_name.contains("mem")
684}
685
686fn format_bytes_human_readable(bytes: usize) -> String {
687 format!("{}", ReadableSize(bytes as u64))
688}
689
690impl Display for RecordBatchMetrics {
692 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
693 for metric in &self.plan_metrics {
694 write!(
695 f,
696 "{:indent$}{} metrics=[",
697 " ",
698 metric.plan.trim_end(),
699 indent = metric.level * 2,
700 )?;
701 for (label, value) in &metric.metrics {
702 if is_time_metric(label) {
703 write!(
704 f,
705 "{}: {}, ",
706 label,
707 format_nanoseconds_human_readable(*value),
708 )?;
709 } else if is_bytes_metric(label) {
710 write!(f, "{}: {}, ", label, format_bytes_human_readable(*value),)?;
711 } else {
712 write!(f, "{}: {}, ", label, value)?;
713 }
714 }
715 writeln!(f, "]")?;
716 }
717
718 Ok(())
719 }
720}
721
722#[derive(serde::Serialize, serde::Deserialize, Default, Debug, Clone)]
723pub struct PlanMetrics {
724 pub plan: String,
726 #[serde(default)]
728 pub plan_name: String,
729 pub level: usize,
731 pub metrics: Vec<(String, usize)>,
734}
735
736enum AsyncRecordBatchStreamAdapterState {
737 Uninit(FutureStream),
738 Ready(SendableRecordBatchStream),
739 Failed,
740}
741
742pub struct AsyncRecordBatchStreamAdapter {
743 schema: SchemaRef,
744 state: AsyncRecordBatchStreamAdapterState,
745}
746
747impl AsyncRecordBatchStreamAdapter {
748 pub fn new(schema: SchemaRef, stream: FutureStream) -> Self {
749 Self {
750 schema,
751 state: AsyncRecordBatchStreamAdapterState::Uninit(stream),
752 }
753 }
754}
755
756impl RecordBatchStream for AsyncRecordBatchStreamAdapter {
757 fn schema(&self) -> SchemaRef {
758 self.schema.clone()
759 }
760
761 fn output_ordering(&self) -> Option<&[OrderOption]> {
762 None
763 }
764
765 fn metrics(&self) -> Option<RecordBatchMetrics> {
766 None
767 }
768}
769
770impl Stream for AsyncRecordBatchStreamAdapter {
771 type Item = Result<RecordBatch>;
772
773 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
774 loop {
775 match &mut self.state {
776 AsyncRecordBatchStreamAdapterState::Uninit(stream_future) => {
777 match ready!(Pin::new(stream_future).poll(cx)) {
778 Ok(stream) => {
779 self.state = AsyncRecordBatchStreamAdapterState::Ready(stream);
780 continue;
781 }
782 Err(e) => {
783 self.state = AsyncRecordBatchStreamAdapterState::Failed;
784 return Poll::Ready(Some(Err(e)));
785 }
786 };
787 }
788 AsyncRecordBatchStreamAdapterState::Ready(stream) => {
789 return Poll::Ready(ready!(Pin::new(stream).poll_next(cx)));
790 }
791 AsyncRecordBatchStreamAdapterState::Failed => return Poll::Ready(None),
792 }
793 }
794 }
795
796 #[inline]
798 fn size_hint(&self) -> (usize, Option<usize>) {
799 (0, None)
800 }
801}
802
803fn custom_cast(
805 array: &dyn Array,
806 target_type: &ArrowDataType,
807 extype: Option<ColumnExtType>,
808) -> std::result::Result<Arc<dyn Array>, ArrowError> {
809 if let ArrowDataType::Map(_, _) = array.data_type()
810 && let ArrowDataType::Binary = target_type
811 {
812 return convert_map_to_json_binary(array, extype);
813 }
814
815 cast(array, target_type)
816}
817
818fn convert_map_to_json_binary(
820 array: &dyn Array,
821 extype: Option<ColumnExtType>,
822) -> std::result::Result<Arc<dyn Array>, ArrowError> {
823 use datatypes::arrow::array::{BinaryArray, MapArray};
824 use serde_json::Value;
825
826 let map_array = array
827 .as_any()
828 .downcast_ref::<MapArray>()
829 .ok_or_else(|| ArrowError::CastError("Failed to downcast to MapArray".to_string()))?;
830
831 let mut json_values = Vec::with_capacity(map_array.len());
832
833 for i in 0..map_array.len() {
834 if map_array.is_null(i) {
835 json_values.push(None);
836 } else {
837 let map_entry = map_array.value(i);
839 let key_value_array = map_entry
840 .as_any()
841 .downcast_ref::<datatypes::arrow::array::StructArray>()
842 .ok_or_else(|| {
843 ArrowError::CastError("Failed to downcast to StructArray".to_string())
844 })?;
845
846 let mut json_obj = serde_json::Map::with_capacity(key_value_array.len());
848
849 for j in 0..key_value_array.len() {
850 if key_value_array.is_null(j) {
851 continue;
852 }
853 let key_field = key_value_array.column(0);
854 let value_field = key_value_array.column(1);
855
856 if key_field.is_null(j) {
857 continue;
858 }
859
860 let key = key_field
861 .as_any()
862 .downcast_ref::<datatypes::arrow::array::StringArray>()
863 .ok_or_else(|| {
864 ArrowError::CastError("Failed to downcast key to StringArray".to_string())
865 })?
866 .value(j);
867
868 let value = if value_field.is_null(j) {
869 Value::Null
870 } else {
871 let value_str = value_field
872 .as_any()
873 .downcast_ref::<datatypes::arrow::array::StringArray>()
874 .ok_or_else(|| {
875 ArrowError::CastError(
876 "Failed to downcast value to StringArray".to_string(),
877 )
878 })?
879 .value(j);
880 Value::String(value_str.to_string())
881 };
882
883 json_obj.insert(key.to_string(), value);
884 }
885
886 let json_value = Value::Object(json_obj);
887 let json_bytes = match extype {
888 Some(ColumnExtType::Json) => {
889 let json_string = match serde_json::to_string(&json_value) {
890 Ok(s) => s,
891 Err(e) => {
892 return Err(ArrowError::CastError(format!(
893 "Failed to serialize JSON: {}",
894 e
895 )));
896 }
897 };
898 match jsonb::parse_value(json_string.as_bytes()) {
899 Ok(jsonb_value) => jsonb_value.to_vec(),
900 Err(e) => {
901 return Err(ArrowError::CastError(format!(
902 "Failed to serialize JSONB: {}",
903 e
904 )));
905 }
906 }
907 }
908 _ => match serde_json::to_vec(&json_value) {
909 Ok(b) => b,
910 Err(e) => {
911 return Err(ArrowError::CastError(format!(
912 "Failed to serialize JSON: {}",
913 e
914 )));
915 }
916 },
917 };
918 json_values.push(Some(json_bytes));
919 }
920 }
921
922 let binary_array = BinaryArray::from_iter(json_values);
923 Ok(Arc::new(binary_array))
924}
925
926#[cfg(test)]
927mod test {
928 use std::any::Any;
929 use std::time::Duration;
930
931 use common_error::ext::BoxedError;
932 use common_error::mock::MockError;
933 use common_error::status_code::StatusCode;
934 use datafusion::execution::TaskContext;
935 use datafusion::physical_expr::{EquivalenceProperties, Partitioning};
936 use datafusion::physical_plan::execution_plan::{Boundedness, EmissionType};
937 use datafusion::physical_plan::metrics::{ExecutionPlanMetricsSet, MetricBuilder, MetricsSet};
938 use datafusion::physical_plan::{DisplayAs, PlanProperties};
939 use datatypes::arrow::array::{ArrayRef, MapArray, StringArray, StructArray};
940 use datatypes::arrow::buffer::OffsetBuffer;
941 use datatypes::arrow::datatypes::Field;
942 use datatypes::prelude::ConcreteDataType;
943 use datatypes::schema::ColumnSchema;
944 use datatypes::vectors::Int32Vector;
945 use futures::StreamExt;
946 use serde_json::json;
947 use snafu::IntoError;
948
949 use super::*;
950 use crate::RecordBatches;
951 use crate::error::Error;
952
953 #[derive(Debug)]
954 struct TestMetricsExec {
955 properties: Arc<PlanProperties>,
956 metrics: ExecutionPlanMetricsSet,
957 }
958
959 impl TestMetricsExec {
960 fn new(schema: DfSchemaRef) -> Self {
961 Self::with_output_bytes(schema, &[24])
962 }
963
964 fn with_output_bytes(schema: DfSchemaRef, output_bytes_by_partition: &[usize]) -> Self {
965 let metrics = ExecutionPlanMetricsSet::new();
966 let elapsed_compute = MetricBuilder::new(&metrics).elapsed_compute(0);
967 elapsed_compute.add_duration(Duration::from_nanos(42));
968 for (partition, output_bytes) in output_bytes_by_partition.iter().copied().enumerate() {
969 let metric = MetricBuilder::new(&metrics).output_bytes(partition);
970 metric.add(output_bytes);
971 }
972
973 Self {
974 properties: Arc::new(PlanProperties::new(
975 EquivalenceProperties::new(schema),
976 Partitioning::UnknownPartitioning(output_bytes_by_partition.len().max(1)),
977 EmissionType::Incremental,
978 Boundedness::Bounded,
979 )),
980 metrics,
981 }
982 }
983 }
984
985 impl DisplayAs for TestMetricsExec {
986 fn fmt_as(&self, _t: DisplayFormatType, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
987 panic!("non-verbose lightweight partial metrics must not format the plan")
988 }
989 }
990
991 impl ExecutionPlan for TestMetricsExec {
992 fn name(&self) -> &str {
993 REGION_SCAN_EXEC_NAME
994 }
995
996 fn as_any(&self) -> &dyn Any {
997 self
998 }
999
1000 fn properties(&self) -> &Arc<PlanProperties> {
1001 &self.properties
1002 }
1003
1004 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
1005 vec![]
1006 }
1007
1008 fn with_new_children(
1009 self: Arc<Self>,
1010 _children: Vec<Arc<dyn ExecutionPlan>>,
1011 ) -> datafusion_common::Result<Arc<dyn ExecutionPlan>> {
1012 Ok(self)
1013 }
1014
1015 fn execute(
1016 &self,
1017 _partition: usize,
1018 _context: Arc<TaskContext>,
1019 ) -> datafusion_common::Result<DfSendableRecordBatchStream> {
1020 unreachable!("the test passes a separate stream to RecordBatchStreamAdapter")
1021 }
1022
1023 fn metrics(&self) -> Option<MetricsSet> {
1024 Some(self.metrics.clone_inner())
1025 }
1026 }
1027
1028 #[test]
1029 fn test_lightweight_query_load_metrics_sums_region_scan_output_bytes() {
1030 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
1031 "a",
1032 ConcreteDataType::int32_datatype(),
1033 false,
1034 )]));
1035 let plan = TestMetricsExec::with_output_bytes(schema.arrow_schema().clone(), &[24, 18]);
1036
1037 let metrics = collect_lightweight_query_load_metrics(&plan, Some(42));
1038
1039 assert_eq!(metrics.query_load_region_id, Some(42));
1040 assert_eq!(region_scan_output_bytes(&metrics), 42);
1041 assert_eq!(metrics.plan_metrics.len(), 1);
1042 assert_eq!(
1043 metrics.plan_metrics[0].metrics,
1044 vec![("output_bytes".to_string(), 42)]
1045 );
1046 }
1047
1048 #[tokio::test]
1049 async fn test_record_batch_stream_adapter_collects_lightweight_partial_metrics() {
1050 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
1051 "a",
1052 ConcreteDataType::int32_datatype(),
1053 false,
1054 )]));
1055 let batch1 = RecordBatch::new(
1056 schema.clone(),
1057 vec![Arc::new(Int32Vector::from_slice([1])) as _],
1058 )
1059 .unwrap()
1060 .into_df_record_batch();
1061 let batch2 = RecordBatch::new(
1062 schema.clone(),
1063 vec![Arc::new(Int32Vector::from_slice([2])) as _],
1064 )
1065 .unwrap()
1066 .into_df_record_batch();
1067 let df_stream = Box::pin(
1068 datafusion::physical_plan::stream::RecordBatchStreamAdapter::new(
1069 schema.arrow_schema().clone(),
1070 futures::stream::iter(vec![Ok(batch1), Ok(batch2)]),
1071 ),
1072 );
1073 let plan = Arc::new(TestMetricsExec::new(schema.arrow_schema().clone()));
1074
1075 let mut adapter = RecordBatchStreamAdapter::try_new(df_stream).unwrap();
1076 adapter.set_metrics2(plan);
1077 adapter.set_query_load_region_id(Some(42));
1078
1079 assert!(adapter.metrics().is_none());
1080 assert!(adapter.next().await.unwrap().is_ok());
1081 let metrics = adapter
1082 .metrics()
1083 .expect("non-verbose queries need partial query-load metrics before EOF");
1084 assert_eq!(metrics.elapsed_compute, 42);
1085 assert_eq!(metrics.query_load_region_id, Some(42));
1086 assert_eq!(region_scan_output_bytes(&metrics), 24);
1087 assert_eq!(metrics.plan_metrics.len(), 1);
1088 assert_eq!(metrics.plan_metrics[0].plan, REGION_SCAN_EXEC_NAME);
1089 assert_eq!(metrics.plan_metrics[0].plan_name, REGION_SCAN_EXEC_NAME);
1090 assert_eq!(
1091 metrics.plan_metrics[0].metrics,
1092 vec![("output_bytes".to_string(), 24)]
1093 );
1094 }
1095
1096 #[test]
1097 fn test_record_batch_stream_adapter_reuses_partial_query_stats_on_drop() {
1098 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
1099 "a",
1100 ConcreteDataType::int32_datatype(),
1101 false,
1102 )]));
1103 let df_stream = Box::pin(
1104 datafusion::physical_plan::stream::RecordBatchStreamAdapter::new(
1105 schema.arrow_schema().clone(),
1106 futures::stream::empty::<datafusion::error::Result<DfRecordBatch>>(),
1107 ),
1108 );
1109 let counters = RegionQueryStatCounters {
1110 query_cpu_time: Arc::new(AtomicU64::new(10)),
1111 query_scanned_bytes: Arc::new(AtomicU64::new(20)),
1112 };
1113 let stale_metrics = RecordBatchMetrics {
1114 elapsed_compute: 1,
1115 plan_metrics: vec![PlanMetrics {
1116 plan: REGION_SCAN_EXEC_NAME.to_string(),
1117 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1118 level: 0,
1119 metrics: vec![("output_bytes".to_string(), 2)],
1120 }],
1121 ..Default::default()
1122 };
1123 let adapter = RecordBatchStreamAdapter {
1124 schema: schema.clone(),
1125 stream: df_stream,
1126 metrics: None,
1127 metrics_2: Metrics::PartialResolved(
1128 Arc::new(TestMetricsExec::new(schema.arrow_schema().clone())),
1129 stale_metrics,
1130 ),
1131 query_load_region_id: None,
1132 query_stat_counters: Some(counters.clone()),
1133 explain_verbose: false,
1134 span: Span::current(),
1135 };
1136
1137 drop(adapter);
1138
1139 assert_eq!(counters.query_cpu_time.load(Ordering::Relaxed), 11);
1140 assert_eq!(counters.query_scanned_bytes.load(Ordering::Relaxed), 22);
1141 }
1142
1143 #[tokio::test]
1144 async fn test_async_recordbatch_stream_adaptor() {
1145 struct MaybeErrorRecordBatchStream {
1146 items: Vec<Result<RecordBatch>>,
1147 }
1148
1149 impl RecordBatchStream for MaybeErrorRecordBatchStream {
1150 fn schema(&self) -> SchemaRef {
1151 unimplemented!()
1152 }
1153
1154 fn output_ordering(&self) -> Option<&[OrderOption]> {
1155 None
1156 }
1157
1158 fn metrics(&self) -> Option<RecordBatchMetrics> {
1159 None
1160 }
1161 }
1162
1163 impl Stream for MaybeErrorRecordBatchStream {
1164 type Item = Result<RecordBatch>;
1165
1166 fn poll_next(
1167 mut self: Pin<&mut Self>,
1168 _: &mut Context<'_>,
1169 ) -> Poll<Option<Self::Item>> {
1170 if let Some(batch) = self.items.pop() {
1171 Poll::Ready(Some(Ok(batch?)))
1172 } else {
1173 Poll::Ready(None)
1174 }
1175 }
1176 }
1177
1178 fn new_future_stream(
1179 maybe_recordbatches: Result<Vec<Result<RecordBatch>>>,
1180 ) -> FutureStream {
1181 Box::pin(async move {
1182 maybe_recordbatches
1183 .map(|items| Box::pin(MaybeErrorRecordBatchStream { items }) as _)
1184 })
1185 }
1186
1187 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
1188 "a",
1189 ConcreteDataType::int32_datatype(),
1190 false,
1191 )]));
1192 let batch1 = RecordBatch::new(
1193 schema.clone(),
1194 vec![Arc::new(Int32Vector::from_slice([1])) as _],
1195 )
1196 .unwrap();
1197 let batch2 = RecordBatch::new(
1198 schema.clone(),
1199 vec![Arc::new(Int32Vector::from_slice([2])) as _],
1200 )
1201 .unwrap();
1202
1203 let success_stream = new_future_stream(Ok(vec![Ok(batch1.clone()), Ok(batch2.clone())]));
1204 let adapter = AsyncRecordBatchStreamAdapter::new(schema.clone(), success_stream);
1205 let collected = RecordBatches::try_collect(Box::pin(adapter)).await.unwrap();
1206 assert_eq!(
1207 collected,
1208 RecordBatches::try_new(schema.clone(), vec![batch2.clone(), batch1.clone()]).unwrap()
1209 );
1210
1211 let poll_err_stream = new_future_stream(Ok(vec![
1212 Ok(batch1.clone()),
1213 Err(error::ExternalSnafu
1214 .into_error(BoxedError::new(MockError::new(StatusCode::Unknown)))),
1215 ]));
1216 let adapter = AsyncRecordBatchStreamAdapter::new(schema.clone(), poll_err_stream);
1217 let err = RecordBatches::try_collect(Box::pin(adapter))
1218 .await
1219 .unwrap_err();
1220 assert!(
1221 matches!(err, Error::External { .. }),
1222 "unexpected err {err}"
1223 );
1224
1225 let failed_to_init_stream =
1226 new_future_stream(Err(error::ExternalSnafu
1227 .into_error(BoxedError::new(MockError::new(StatusCode::Internal)))));
1228 let adapter = AsyncRecordBatchStreamAdapter::new(schema.clone(), failed_to_init_stream);
1229 let err = RecordBatches::try_collect(Box::pin(adapter))
1230 .await
1231 .unwrap_err();
1232 assert!(
1233 matches!(err, Error::External { .. }),
1234 "unexpected err {err}"
1235 );
1236 }
1237
1238 #[test]
1239 fn test_convert_map_to_json_binary() {
1240 let keys = StringArray::from(vec![Some("a"), Some("b"), Some("c"), Some("x")]);
1241 let values = StringArray::from(vec![Some("1"), None, Some("3"), Some("42")]);
1242 let key_field = Arc::new(Field::new("key", ArrowDataType::Utf8, false));
1243 let value_field = Arc::new(Field::new("value", ArrowDataType::Utf8, true));
1244 let struct_type = ArrowDataType::Struct(vec![key_field, value_field].into());
1245
1246 let entries_field = Arc::new(Field::new("entries", struct_type, false));
1247
1248 let struct_array = StructArray::from(vec![
1249 (
1250 Arc::new(Field::new("key", ArrowDataType::Utf8, false)),
1251 Arc::new(keys) as ArrayRef,
1252 ),
1253 (
1254 Arc::new(Field::new("value", ArrowDataType::Utf8, true)),
1255 Arc::new(values) as ArrayRef,
1256 ),
1257 ]);
1258
1259 let offsets = OffsetBuffer::from_lengths([3, 0, 1]);
1260 let nulls = datatypes::arrow::buffer::NullBuffer::from(vec![true, false, true]);
1261
1262 let map_array = MapArray::new(
1263 entries_field,
1264 offsets,
1265 struct_array,
1266 Some(nulls), false,
1268 );
1269
1270 let result = convert_map_to_json_binary(&map_array, None).unwrap();
1271 let binary_array = result
1272 .as_any()
1273 .downcast_ref::<datatypes::arrow::array::BinaryArray>()
1274 .unwrap();
1275
1276 let expected_jsons = [
1277 Some(r#"{"a":"1","b":null,"c":"3"}"#),
1278 None,
1279 Some(r#"{"x":"42"}"#),
1280 ];
1281
1282 for (i, _) in expected_jsons.iter().enumerate() {
1283 if let Some(expected) = &expected_jsons[i] {
1284 assert!(!binary_array.is_null(i));
1285 let actual_bytes = binary_array.value(i);
1286 let actual_str = std::str::from_utf8(actual_bytes).unwrap();
1287 assert_eq!(actual_str, *expected);
1288 } else {
1289 assert!(binary_array.is_null(i));
1290 }
1291 }
1292
1293 let result_json =
1294 convert_map_to_json_binary(&map_array, Some(ColumnExtType::Json)).unwrap();
1295 let binary_array_json = result_json
1296 .as_any()
1297 .downcast_ref::<datatypes::arrow::array::BinaryArray>()
1298 .unwrap();
1299
1300 for (i, _) in expected_jsons.iter().enumerate() {
1301 if expected_jsons[i].is_some() {
1302 assert!(!binary_array_json.is_null(i));
1303 let actual_bytes = binary_array_json.value(i);
1304 assert_ne!(actual_bytes, expected_jsons[i].unwrap().as_bytes());
1305 } else {
1306 assert!(binary_array_json.is_null(i));
1307 }
1308 }
1309 }
1310
1311 #[test]
1312 fn test_record_query_stats_updates_region_counters() {
1313 let counters = RegionQueryStatCounters {
1314 query_cpu_time: Arc::new(AtomicU64::new(10)),
1315 query_scanned_bytes: Arc::new(AtomicU64::new(20)),
1316 };
1317 let metrics = RecordBatchMetrics {
1318 elapsed_compute: 2_000_000,
1319 plan_metrics: vec![PlanMetrics {
1320 plan: "RegionScanExec: region=1".to_string(),
1321 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1322 level: 0,
1323 metrics: vec![("output_bytes".to_string(), 42)],
1324 }],
1325 ..Default::default()
1326 };
1327
1328 record_query_stats(&counters, &metrics);
1329
1330 assert_eq!(counters.query_cpu_time.load(Ordering::Relaxed), 2_000_010);
1331 assert_eq!(counters.query_scanned_bytes.load(Ordering::Relaxed), 62);
1332 }
1333
1334 #[test]
1335 fn test_record_batch_stream_adapter_records_query_stats_on_drop() {
1336 let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
1337 "a",
1338 ConcreteDataType::int32_datatype(),
1339 false,
1340 )]));
1341 let df_stream = Box::pin(
1342 datafusion::physical_plan::stream::RecordBatchStreamAdapter::new(
1343 schema.arrow_schema().clone(),
1344 futures::stream::empty::<datafusion::error::Result<DfRecordBatch>>(),
1345 ),
1346 );
1347 let counters = RegionQueryStatCounters {
1348 query_cpu_time: Arc::new(AtomicU64::new(10)),
1349 query_scanned_bytes: Arc::new(AtomicU64::new(20)),
1350 };
1351 let metrics = RecordBatchMetrics {
1352 elapsed_compute: 2_000_000,
1353 plan_metrics: vec![PlanMetrics {
1354 plan: "RegionScanExec: region=1".to_string(),
1355 plan_name: REGION_SCAN_EXEC_NAME.to_string(),
1356 level: 0,
1357 metrics: vec![("output_bytes".to_string(), 42)],
1358 }],
1359 ..Default::default()
1360 };
1361 let adapter = RecordBatchStreamAdapter {
1362 schema,
1363 stream: df_stream,
1364 metrics: None,
1365 metrics_2: Metrics::Resolved(metrics),
1366 query_load_region_id: None,
1367 query_stat_counters: Some(counters.clone()),
1368 explain_verbose: false,
1369 span: Span::current(),
1370 };
1371
1372 drop(adapter);
1373
1374 assert_eq!(counters.query_cpu_time.load(Ordering::Relaxed), 2_000_010);
1375 assert_eq!(counters.query_scanned_bytes.load(Ordering::Relaxed), 62);
1376 }
1377
1378 #[test]
1379 fn test_recordbatch_metrics_deserializes_without_region_watermarks() {
1380 let metrics: RecordBatchMetrics = serde_json::from_value(json!({
1381 "elapsed_compute": 12,
1382 "memory_usage": 34,
1383 "plan_metrics": []
1384 }))
1385 .unwrap();
1386
1387 assert!(metrics.region_watermarks.is_empty());
1388 assert_eq!(metrics.elapsed_compute, 12);
1389 assert_eq!(metrics.memory_usage, 34);
1390 }
1391
1392 #[test]
1393 fn test_plan_metrics_deserializes_without_plan_name() {
1394 let metrics: RecordBatchMetrics = serde_json::from_value(json!({
1395 "elapsed_compute": 12,
1396 "memory_usage": 34,
1397 "plan_metrics": [{
1398 "plan": "SeqScan: region=1",
1399 "level": 0,
1400 "metrics": []
1401 }]
1402 }))
1403 .unwrap();
1404
1405 assert_eq!(metrics.plan_metrics[0].plan_name, "");
1406 }
1407
1408 #[test]
1409 fn test_recordbatch_metrics_region_watermarks_serde_roundtrip() {
1410 let metrics = RecordBatchMetrics {
1411 region_watermarks: vec![
1412 RegionWatermarkEntry {
1413 region_id: 1,
1414 watermark: Some(100),
1415 },
1416 RegionWatermarkEntry {
1417 region_id: 2,
1418 watermark: None,
1419 },
1420 ],
1421 ..Default::default()
1422 };
1423
1424 let value = serde_json::to_value(&metrics).unwrap();
1425 assert_eq!(
1426 value.get("region_watermarks").unwrap(),
1427 &json!([
1428 { "region_id": 1, "watermark": 100 },
1429 { "region_id": 2 }
1430 ])
1431 );
1432
1433 let decoded: RecordBatchMetrics = serde_json::from_value(value).unwrap();
1434 assert_eq!(decoded.region_watermarks, metrics.region_watermarks);
1435 }
1436
1437 #[test]
1438 fn test_recordbatch_metrics_skips_empty_region_watermarks_on_serialize() {
1439 let value = serde_json::to_value(RecordBatchMetrics::default()).unwrap();
1440 assert!(value.get("region_watermarks").is_none());
1441 }
1442}