1mod error;
18mod json_expr_planner;
19mod planner;
20
21use std::any::Any;
22use std::collections::HashMap;
23use std::sync::Arc;
24
25use async_trait::async_trait;
26use common_base::Plugins;
27use common_catalog::consts::is_readonly_table;
28use common_error::ext::BoxedError;
29use common_function::function::FunctionContext;
30use common_function::function_factory::ScalarFunctionFactory;
31use common_query::{Output, OutputData, OutputMeta};
32use common_recordbatch::adapter::{RecordBatchStreamAdapter, RegionQueryStatCounters};
33use common_recordbatch::{EmptyRecordBatchStream, RecordBatch, SendableRecordBatchStream};
34use common_telemetry::tracing;
35use datafusion::catalog::TableFunction;
36use datafusion::dataframe::DataFrame;
37use datafusion::physical_plan::ExecutionPlan;
38use datafusion::physical_plan::analyze::AnalyzeExec;
39use datafusion::physical_plan::coalesce_partitions::CoalescePartitionsExec;
40use datafusion_common::ResolvedTableReference;
41use datafusion_expr::{
42 AggregateUDF, DmlStatement, LogicalPlan as DfLogicalPlan, LogicalPlan, WindowUDF, WriteOp,
43};
44use datatypes::prelude::VectorRef;
45use datatypes::schema::Schema;
46use futures_util::StreamExt;
47use futures_util::future::try_join;
48use session::context::QueryContextRef;
49use snafu::{OptionExt, ResultExt, ensure};
50use sqlparser::ast::AnalyzeFormat;
51use table::TableRef;
52use table::requests::{DeleteRequest, InsertRequest};
53use table::table::scan::{REGION_SCAN_EXEC_NAME, RegionScanExec};
54use tracing::Span;
55
56use crate::analyze::DistAnalyzeExec;
57pub use crate::datafusion::planner::DfContextProviderAdapter;
58use crate::dist_plan::{
59 DistPlannerOptions, MergeScanLogicalPlan, RemoteDynFilterReceiverInjectorRef,
60};
61use crate::error::{
62 CatalogSnafu, CreateRecordBatchSnafu, MissingTableMutationHandlerSnafu,
63 MissingTimestampColumnSnafu, QueryExecutionSnafu, Result, TableMutationSnafu,
64 TableNotFoundSnafu, TableReadOnlySnafu, UnsupportedExprSnafu,
65};
66use crate::executor::QueryExecutor;
67use crate::metrics::{
68 OnDone, QUERY_STAGE_ELAPSED, maybe_attach_region_watermark_metrics,
69 should_collect_region_watermark_from_query_ctx,
70};
71use crate::options::ScheduledTimeExtension;
72use crate::physical_wrapper::PhysicalPlanWrapperRef;
73use crate::planner::{DfLogicalPlanner, LogicalPlanner};
74use crate::query_engine::{DescribeResult, QueryEngineContext, QueryEngineState};
75use crate::{QueryEngine, metrics};
76
77pub const QUERY_PARALLELISM_HINT: &str = "query_parallelism";
80
81pub const QUERY_FALLBACK_HINT: &str = "query_fallback";
83
84async fn forward_record_batches(
87 mut stream: SendableRecordBatchStream,
88 batch_tx: tokio::sync::mpsc::UnboundedSender<Result<RecordBatch>>,
89) -> Result<()> {
90 while let Some(batch) = stream.next().await {
91 match batch.context(CreateRecordBatchSnafu) {
92 Ok(batch) => {
93 if batch_tx.send(Ok(batch)).is_err() {
94 break;
95 }
96 tokio::task::yield_now().await;
97 }
98 Err(error) => {
99 let _ = batch_tx.send(Err(error));
100 break;
101 }
102 }
103 }
104 Ok(())
105}
106
107fn query_load_region_id(plan: &Arc<dyn ExecutionPlan>) -> Option<u64> {
108 let mut region_id = None;
109 let mut stack = vec![plan.clone()];
110
111 while let Some(plan) = stack.pop() {
112 if plan.name() == REGION_SCAN_EXEC_NAME
113 && let Some(scan) = plan.as_any().downcast_ref::<RegionScanExec>()
114 && let Some(scan_region_id) = scan.query_load_region_id()
115 {
116 match region_id {
117 Some(region_id) if region_id != scan_region_id => return None,
118 Some(_) => {}
119 None => region_id = Some(scan_region_id),
120 }
121 }
122 stack.extend(plan.children().into_iter().cloned());
123 }
124
125 region_id
126}
127
128fn query_stat_counters(plan: &Arc<dyn ExecutionPlan>) -> Option<RegionQueryStatCounters> {
137 let mut counters: Option<RegionQueryStatCounters> = None;
138 let mut stack = vec![plan.clone()];
139
140 while let Some(plan) = stack.pop() {
141 if plan.name() == REGION_SCAN_EXEC_NAME
142 && let Some(scan) = plan.as_any().downcast_ref::<RegionScanExec>()
143 && let Some(scan_counters) = scan.query_stat_counters()
144 {
145 match &counters {
146 Some(counters)
147 if !Arc::ptr_eq(&counters.query_cpu_time, &scan_counters.query_cpu_time)
148 || !Arc::ptr_eq(
149 &counters.query_scanned_bytes,
150 &scan_counters.query_scanned_bytes,
151 ) =>
152 {
153 return None;
154 }
155 Some(_) => {}
156 None => counters = Some(scan_counters),
157 }
158 }
159 stack.extend(plan.children().into_iter().cloned());
160 }
161
162 counters
163}
164
165pub struct DatafusionQueryEngine {
166 state: Arc<QueryEngineState>,
167 plugins: Plugins,
168}
169
170impl DatafusionQueryEngine {
171 pub fn new(state: Arc<QueryEngineState>, plugins: Plugins) -> Self {
172 Self { state, plugins }
173 }
174
175 #[tracing::instrument(skip_all)]
176 async fn exec_query_plan(
177 &self,
178 plan: LogicalPlan,
179 query_ctx: QueryContextRef,
180 ) -> Result<Output> {
181 let mut ctx = self.engine_context(query_ctx.clone());
182 let plan = if let Some(receiver_injector) =
183 self.plugins.get::<RemoteDynFilterReceiverInjectorRef>()
184 {
185 receiver_injector.maybe_inject(plan, query_ctx.clone())
186 } else {
187 plan
188 };
189
190 let physical_plan = self.create_physical_plan(&mut ctx, &plan).await?;
192 let physical_plan = self.optimize_physical_plan(&mut ctx, physical_plan)?;
193 let physical_plan = if let Some(wrapper) = self.plugins.get::<PhysicalPlanWrapperRef>() {
194 wrapper.wrap(physical_plan, query_ctx)
195 } else {
196 physical_plan
197 };
198
199 let stream = self.execute_stream(&ctx, &physical_plan)?;
200
201 Ok(Output::new(
202 OutputData::Stream(stream),
203 OutputMeta::new_with_plan(physical_plan),
204 ))
205 }
206
207 #[tracing::instrument(skip_all)]
208 async fn exec_dml_statement(
209 &self,
210 dml: DmlStatement,
211 query_ctx: QueryContextRef,
212 ) -> Result<Output> {
213 ensure!(
214 matches!(dml.op, WriteOp::Insert(_) | WriteOp::Delete),
215 UnsupportedExprSnafu {
216 name: format!("DML op {}", dml.op),
217 }
218 );
219
220 let _timer = QUERY_STAGE_ELAPSED
221 .with_label_values(&[dml.op.name()])
222 .start_timer();
223
224 let default_catalog = &query_ctx.current_catalog().to_owned();
225 let default_schema = &query_ctx.current_schema();
226 let table_name = dml.table_name.resolve(default_catalog, default_schema);
227 let table = self.find_table(&table_name, &query_ctx).await?;
228
229 let Output { data, meta } = self
230 .exec_query_plan((*dml.input).clone(), query_ctx.clone())
231 .await?;
232 let stream = match data {
233 OutputData::RecordBatches(batches) => batches.as_stream(),
234 OutputData::Stream(stream) => stream,
235 _ => unreachable!(),
236 };
237
238 let mut affected_rows = 0;
239 let mut insert_cost = 0;
240
241 match dml.op {
242 WriteOp::Insert(_) => {
243 let (batch_tx, batch_rx) = tokio::sync::mpsc::unbounded_channel();
244 let producer = forward_record_batches(stream, batch_tx);
245 let consumer = self.consume_insert_record_batches(
246 batch_rx,
247 &table_name,
248 table.schema(),
249 query_ctx.clone(),
250 );
251 let ((), (rows, cost)) = try_join(producer, consumer).await?;
252 affected_rows += rows;
253 insert_cost += cost;
254 }
255 WriteOp::Delete => {
256 let (batch_tx, batch_rx) = tokio::sync::mpsc::unbounded_channel();
259 let producer = forward_record_batches(stream, batch_tx);
260 let consumer = self.consume_delete_record_batches(
261 batch_rx,
262 &table_name,
263 &table,
264 query_ctx.clone(),
265 );
266 let (rows, ()) = try_join(consumer, producer).await?;
267 affected_rows += rows;
268 }
269 _ => unreachable!("guarded by the 'ensure!' at the beginning"),
270 }
271 Ok(Output::new(
272 OutputData::AffectedRows(affected_rows),
273 OutputMeta::new(meta.plan, insert_cost),
274 ))
275 }
276
277 async fn consume_insert_record_batches(
278 &self,
279 mut batch_rx: tokio::sync::mpsc::UnboundedReceiver<Result<RecordBatch>>,
280 table_name: &ResolvedTableReference,
281 table_schema: Arc<Schema>,
282 query_ctx: QueryContextRef,
283 ) -> Result<(usize, usize)> {
284 let mut affected_rows = 0;
285 let mut insert_cost = 0;
286 while let Some(batch) = batch_rx.recv().await {
287 let batch = batch?;
288 let column_vectors = batch
289 .column_vectors(&table_name.to_string(), table_schema.clone())
290 .map_err(BoxedError::new)
291 .context(QueryExecutionSnafu)?;
292 let output = self
294 .insert(table_name, column_vectors, query_ctx.clone())
295 .await?;
296 let (rows, cost) = output.extract_rows_and_cost();
297 affected_rows += rows;
298 insert_cost += cost;
299 }
300 Ok((affected_rows, insert_cost))
301 }
302
303 async fn consume_delete_record_batches(
304 &self,
305 mut batch_rx: tokio::sync::mpsc::UnboundedReceiver<Result<RecordBatch>>,
306 table_name: &ResolvedTableReference,
307 table: &TableRef,
308 query_ctx: QueryContextRef,
309 ) -> Result<usize> {
310 let mut affected_rows = 0;
311 while let Some(batch) = batch_rx.recv().await {
312 let batch = batch?;
313 let column_vectors = batch
314 .column_vectors(&table_name.to_string(), table.schema())
315 .map_err(BoxedError::new)
316 .context(QueryExecutionSnafu)?;
317 affected_rows += self
318 .delete(table_name, table, column_vectors, query_ctx.clone())
319 .await?;
320 }
321 Ok(affected_rows)
322 }
323
324 #[tracing::instrument(skip_all)]
325 async fn delete(
326 &self,
327 table_name: &ResolvedTableReference,
328 table: &TableRef,
329 column_vectors: HashMap<String, VectorRef>,
330 query_ctx: QueryContextRef,
331 ) -> Result<usize> {
332 let catalog_name = table_name.catalog.to_string();
333 let schema_name = table_name.schema.to_string();
334 let table_name = table_name.table.to_string();
335 let table_schema = table.schema();
336
337 ensure!(
338 !is_readonly_table(&schema_name, &table_name),
339 TableReadOnlySnafu { table: table_name }
340 );
341
342 let ts_column = table_schema
343 .timestamp_column()
344 .map(|x| &x.name)
345 .with_context(|| MissingTimestampColumnSnafu {
346 table_name: table_name.clone(),
347 })?;
348
349 let table_info = table.table_info();
350 let rowkey_columns = table_info
351 .meta
352 .row_key_column_names()
353 .collect::<Vec<&String>>();
354 let column_vectors = column_vectors
355 .into_iter()
356 .filter(|x| &x.0 == ts_column || rowkey_columns.contains(&&x.0))
357 .collect::<HashMap<_, _>>();
358
359 let request = DeleteRequest {
360 catalog_name,
361 schema_name,
362 table_name,
363 key_column_values: column_vectors,
364 };
365
366 self.state
367 .table_mutation_handler()
368 .context(MissingTableMutationHandlerSnafu)?
369 .delete(request, query_ctx)
370 .await
371 .context(TableMutationSnafu)
372 }
373
374 #[tracing::instrument(skip_all)]
375 async fn insert(
376 &self,
377 table_name: &ResolvedTableReference,
378 column_vectors: HashMap<String, VectorRef>,
379 query_ctx: QueryContextRef,
380 ) -> Result<Output> {
381 let catalog_name = table_name.catalog.to_string();
382 let schema_name = table_name.schema.to_string();
383 let table_name = table_name.table.to_string();
384
385 ensure!(
386 !is_readonly_table(&schema_name, &table_name),
387 TableReadOnlySnafu { table: table_name }
388 );
389
390 let request = InsertRequest {
391 catalog_name,
392 schema_name,
393 table_name,
394 columns_values: column_vectors,
395 skip_wal: query_ctx.skip_wal(),
396 };
397
398 self.state
399 .table_mutation_handler()
400 .context(MissingTableMutationHandlerSnafu)?
401 .insert(request, query_ctx)
402 .await
403 .context(TableMutationSnafu)
404 }
405
406 async fn find_table(
407 &self,
408 table_name: &ResolvedTableReference,
409 query_context: &QueryContextRef,
410 ) -> Result<TableRef> {
411 let catalog_name = table_name.catalog.as_ref();
412 let schema_name = table_name.schema.as_ref();
413 let table_name = table_name.table.as_ref();
414
415 self.state
416 .catalog_manager()
417 .table(catalog_name, schema_name, table_name, Some(query_context))
418 .await
419 .context(CatalogSnafu)?
420 .with_context(|| TableNotFoundSnafu { table: table_name })
421 }
422
423 #[tracing::instrument(skip_all)]
424 async fn create_physical_plan(
425 &self,
426 ctx: &mut QueryEngineContext,
427 logical_plan: &LogicalPlan,
428 ) -> Result<Arc<dyn ExecutionPlan>> {
429 #[derive(Debug)]
433 struct PanicLogger<'a> {
434 input_logical_plan: &'a LogicalPlan,
435 after_analyze: Option<LogicalPlan>,
436 after_optimize: Option<LogicalPlan>,
437 phy_plan: Option<Arc<dyn ExecutionPlan>>,
438 }
439 impl Drop for PanicLogger<'_> {
440 fn drop(&mut self) {
441 if std::thread::panicking() {
442 common_telemetry::error!(
443 "Panic while creating physical plan, input logical plan: {:?}, after analyze: {:?}, after optimize: {:?}, final physical plan: {:?}",
444 self.input_logical_plan,
445 self.after_analyze,
446 self.after_optimize,
447 self.phy_plan
448 );
449 }
450 }
451 }
452
453 let mut logger = PanicLogger {
454 input_logical_plan: logical_plan,
455 after_analyze: None,
456 after_optimize: None,
457 phy_plan: None,
458 };
459
460 let _timer = metrics::CREATE_PHYSICAL_ELAPSED.start_timer();
461 let state = ctx.state();
462
463 common_telemetry::debug!("Create physical plan, input plan: {logical_plan}");
464
465 if matches!(logical_plan, DfLogicalPlan::Explain(_)) {
467 return state
468 .create_physical_plan(logical_plan)
469 .await
470 .map_err(Into::into);
471 }
472
473 let analyzed_plan = state.analyzer().execute_and_check(
475 logical_plan.clone(),
476 state.config_options(),
477 |_, _| {},
478 )?;
479
480 logger.after_analyze = Some(analyzed_plan.clone());
481
482 common_telemetry::debug!("Create physical plan, analyzed plan: {analyzed_plan}");
483
484 let optimized_plan = if let DfLogicalPlan::Extension(ext) = &analyzed_plan
486 && ext.node.name() == MergeScanLogicalPlan::name()
487 {
488 analyzed_plan.clone()
489 } else {
490 state
491 .optimizer()
492 .optimize(analyzed_plan, state, |_, _| {})?
493 };
494
495 common_telemetry::debug!("Create physical plan, optimized plan: {optimized_plan}");
496 logger.after_optimize = Some(optimized_plan.clone());
497
498 let physical_plan = state
499 .query_planner()
500 .create_physical_plan(&optimized_plan, state)
501 .await?;
502
503 logger.phy_plan = Some(physical_plan.clone());
504 drop(logger);
505 Ok(physical_plan)
506 }
507
508 #[tracing::instrument(skip_all)]
509 fn optimize_physical_plan(
510 &self,
511 ctx: &mut QueryEngineContext,
512 plan: Arc<dyn ExecutionPlan>,
513 ) -> Result<Arc<dyn ExecutionPlan>> {
514 let _timer = metrics::OPTIMIZE_PHYSICAL_ELAPSED.start_timer();
515
516 let optimized_plan = if let Some(analyze_plan) = plan.as_any().downcast_ref::<AnalyzeExec>()
523 {
524 let format = if let Some(format) = ctx.query_ctx().explain_format()
525 && format.to_lowercase() == "json"
526 {
527 AnalyzeFormat::JSON
528 } else {
529 AnalyzeFormat::TEXT
530 };
531 ctx.query_ctx().set_explain_verbose(analyze_plan.verbose());
534
535 Arc::new(DistAnalyzeExec::new(
536 analyze_plan.input().clone(),
537 analyze_plan.verbose(),
538 format,
539 ))
540 } else {
548 plan
549 };
557
558 Ok(optimized_plan)
559 }
560}
561
562#[async_trait]
563impl QueryEngine for DatafusionQueryEngine {
564 fn as_any(&self) -> &dyn Any {
565 self
566 }
567
568 fn planner(&self) -> Arc<dyn LogicalPlanner> {
569 Arc::new(DfLogicalPlanner::new(self.state.clone()))
570 }
571
572 fn name(&self) -> &str {
573 "datafusion"
574 }
575
576 async fn describe(
577 &self,
578 plan: LogicalPlan,
579 _query_ctx: QueryContextRef,
580 ) -> Result<DescribeResult> {
581 Ok(DescribeResult { logical_plan: plan })
582 }
583
584 async fn execute(&self, plan: LogicalPlan, query_ctx: QueryContextRef) -> Result<Output> {
585 match plan {
586 LogicalPlan::Dml(dml) => self.exec_dml_statement(dml, query_ctx).await,
587 _ => self.exec_query_plan(plan, query_ctx).await,
588 }
589 }
590
591 fn register_aggregate_function(&self, func: AggregateUDF) {
599 self.state.register_aggr_function(func);
600 }
601
602 fn register_scalar_function(&self, func: ScalarFunctionFactory) {
605 self.state.register_scalar_function(func);
606 }
607
608 fn register_table_function(&self, func: Arc<TableFunction>) {
609 self.state.register_table_function(func);
610 }
611
612 fn register_window_function(&self, func: WindowUDF) {
613 self.state.register_window_function(func);
614 }
615
616 fn read_table(&self, table: TableRef) -> Result<DataFrame> {
617 self.state.read_table(table).map_err(Into::into)
618 }
619
620 fn engine_context(&self, query_ctx: QueryContextRef) -> QueryEngineContext {
621 let mut state = self.state.session_state();
622 state.config_mut().set_extension(query_ctx.clone());
623 state.config_mut().set_extension(self.state.clone());
624 if let Some(parallelism) = query_ctx.extension(QUERY_PARALLELISM_HINT) {
627 if let Ok(n) = parallelism.parse::<u64>() {
628 if n > 0 {
629 let new_cfg = state.config().clone().with_target_partitions(n as usize);
630 *state.config_mut() = new_cfg;
631 }
632 } else {
633 common_telemetry::warn!(
634 "Failed to parse query_parallelism: {}, using default value",
635 parallelism
636 );
637 }
638 }
639
640 state.config_mut().options_mut().execution.time_zone =
642 Some(query_ctx.timezone().to_string());
643
644 if query_ctx.configuration_parameter().allow_query_fallback() {
647 state
648 .config_mut()
649 .options_mut()
650 .extensions
651 .insert(DistPlannerOptions {
652 allow_query_fallback: true,
653 });
654 } else if let Some(fallback) = query_ctx.extension(QUERY_FALLBACK_HINT) {
655 if fallback.to_lowercase().parse::<bool>().unwrap_or(false) {
658 state
659 .config_mut()
660 .options_mut()
661 .extensions
662 .insert(DistPlannerOptions {
663 allow_query_fallback: true,
664 });
665 }
666 }
667
668 state
669 .config_mut()
670 .options_mut()
671 .extensions
672 .insert(FunctionContext {
673 query_ctx: query_ctx.clone(),
674 state: self.engine_state().function_state(),
675 });
676
677 state
681 .config_mut()
682 .options_mut()
683 .extensions
684 .insert(ScheduledTimeExtension {
685 scheduled_time: crate::options::scheduled_time_from_ctx(&query_ctx),
686 });
687
688 let config_options = state.config_options().clone();
689 let _ = state
690 .execution_props_mut()
691 .config_options
692 .insert(config_options);
693
694 match crate::options::parse_scheduled_time_datetime(&query_ctx.extensions()) {
698 Ok(Some(scheduled_rt)) => {
699 state.execution_props_mut().query_execution_start_time = Some(scheduled_rt);
700 }
701 Ok(None) => {}
702 Err(err) => {
703 common_telemetry::warn!(err; "Ignoring invalid scheduled time query extension");
704 }
705 }
706
707 QueryEngineContext::new(state, query_ctx)
708 }
709
710 fn engine_state(&self) -> &QueryEngineState {
711 &self.state
712 }
713}
714
715impl QueryExecutor for DatafusionQueryEngine {
716 #[tracing::instrument(skip_all)]
717 fn execute_stream(
718 &self,
719 ctx: &QueryEngineContext,
720 plan: &Arc<dyn ExecutionPlan>,
721 ) -> Result<SendableRecordBatchStream> {
722 let query_ctx = ctx.query_ctx();
723 let explain_verbose = query_ctx.explain_verbose();
724 let should_collect_region_watermark =
725 should_collect_region_watermark_from_query_ctx(&query_ctx)?;
726 let output_partitions = plan.properties().output_partitioning().partition_count();
727 if explain_verbose {
728 common_telemetry::info!("Executing query plan, output_partitions: {output_partitions}");
729 }
730
731 let exec_timer = metrics::EXEC_PLAN_ELAPSED.start_timer();
732 let task_ctx = ctx.build_task_ctx();
733 let span = Span::current();
734
735 match plan.properties().output_partitioning().partition_count() {
736 0 => {
737 let schema = Arc::new(
738 Schema::try_from(plan.schema())
739 .map_err(BoxedError::new)
740 .context(QueryExecutionSnafu)?,
741 );
742 Ok(Box::pin(EmptyRecordBatchStream::new(schema)))
743 }
744 1 => {
745 let df_stream = plan.execute(0, task_ctx)?;
746 let mut stream = RecordBatchStreamAdapter::try_new_with_span(df_stream, span)
747 .context(error::ConvertDfRecordBatchStreamSnafu)
748 .map_err(BoxedError::new)
749 .context(QueryExecutionSnafu)?;
750 stream.set_metrics2(plan.clone());
751 stream.set_query_load_region_id(query_load_region_id(plan));
752 stream.set_query_stat_counters(query_stat_counters(plan));
753 stream.set_explain_verbose(explain_verbose);
754 let stream = OnDone::new(Box::pin(stream), move || {
755 let exec_cost = exec_timer.stop_and_record();
756 if explain_verbose {
757 common_telemetry::info!(
758 "DatafusionQueryEngine execute 1 stream, cost: {:?}s",
759 exec_cost,
760 );
761 }
762 });
763 Ok(maybe_attach_region_watermark_metrics(
764 Box::pin(stream),
765 plan.clone(),
766 should_collect_region_watermark,
767 ))
768 }
769 _ => {
770 let merged_plan = CoalescePartitionsExec::new(plan.clone());
772 assert_eq!(
774 1,
775 merged_plan
776 .properties()
777 .output_partitioning()
778 .partition_count()
779 );
780 let df_stream = merged_plan.execute(0, task_ctx)?;
781 let mut stream = RecordBatchStreamAdapter::try_new_with_span(df_stream, span)
782 .context(error::ConvertDfRecordBatchStreamSnafu)
783 .map_err(BoxedError::new)
784 .context(QueryExecutionSnafu)?;
785 stream.set_metrics2(plan.clone());
786 stream.set_query_load_region_id(query_load_region_id(plan));
787 stream.set_query_stat_counters(query_stat_counters(plan));
788 stream.set_explain_verbose(explain_verbose);
789 let stream = OnDone::new(Box::pin(stream), move || {
790 let exec_cost = exec_timer.stop_and_record();
791 if explain_verbose {
792 common_telemetry::info!(
793 "DatafusionQueryEngine execute {output_partitions} stream, cost: {:?}s",
794 exec_cost
795 );
796 }
797 });
798 Ok(maybe_attach_region_watermark_metrics(
799 Box::pin(stream),
800 plan.clone(),
801 should_collect_region_watermark,
802 ))
803 }
804 }
805 }
806}
807
808#[cfg(test)]
809mod tests {
810 use std::fmt;
811 use std::sync::Arc;
812 use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
813
814 use api::v1::SemanticType;
815 use arrow::array::{ArrayRef, UInt64Array};
816 use arrow_schema::SortOptions;
817 use catalog::RegisterTableRequest;
818 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, NUMBERS_TABLE_ID};
819 use common_error::ext::BoxedError;
820 use common_recordbatch::{EmptyRecordBatchStream, SendableRecordBatchStream, util};
821 use datafusion::physical_plan::display::{DisplayAs, DisplayFormatType};
822 use datafusion::physical_plan::expressions::PhysicalSortExpr;
823 use datafusion::physical_plan::joins::{HashJoinExec, JoinOn, PartitionMode};
824 use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet;
825 use datafusion::physical_plan::{ExecutionPlan, PhysicalExpr};
826 use datafusion::prelude::{col, lit};
827 use datafusion_common::{JoinType, NullEquality};
828 use datafusion_physical_expr::expressions::Column;
829 use datatypes::prelude::ConcreteDataType;
830 use datatypes::schema::{ColumnSchema, SchemaRef};
831 use datatypes::vectors::{Helper, UInt32Vector, VectorRef};
832 use session::context::{QueryContext, QueryContextBuilder};
833 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder, RegionMetadataRef};
834 use store_api::region_engine::{
835 PartitionRange, PrepareRequest, QueryScanContext, RegionScanner, ScannerProperties,
836 };
837 use store_api::storage::{RegionId, ScanRequest};
838 use table::table::numbers::{NUMBERS_TABLE_NAME, NumbersTable};
839 use table::table::scan::RegionScanExec;
840
841 use super::*;
842 use crate::options::QueryOptions;
843 use crate::parser::QueryLanguageParser;
844 use crate::part_sort::PartSortExec;
845 use crate::query_engine::{QueryEngineFactory, QueryEngineRef};
846
847 #[derive(Debug)]
848 struct RecordingScanner {
849 schema: SchemaRef,
850 metadata: RegionMetadataRef,
851 properties: ScannerProperties,
852 update_calls: Arc<AtomicUsize>,
853 last_filter_len: Arc<AtomicUsize>,
854 }
855
856 impl RecordingScanner {
857 fn new(
858 schema: SchemaRef,
859 metadata: RegionMetadataRef,
860 update_calls: Arc<AtomicUsize>,
861 last_filter_len: Arc<AtomicUsize>,
862 ) -> Self {
863 Self {
864 schema,
865 metadata,
866 properties: ScannerProperties::default(),
867 update_calls,
868 last_filter_len,
869 }
870 }
871 }
872
873 impl RegionScanner for RecordingScanner {
874 fn name(&self) -> &str {
875 "RecordingScanner"
876 }
877
878 fn properties(&self) -> &ScannerProperties {
879 &self.properties
880 }
881
882 fn schema(&self) -> SchemaRef {
883 self.schema.clone()
884 }
885
886 fn metadata(&self) -> RegionMetadataRef {
887 self.metadata.clone()
888 }
889
890 fn prepare(&mut self, request: PrepareRequest) -> std::result::Result<(), BoxedError> {
891 self.properties.prepare(request);
892 Ok(())
893 }
894
895 fn scan_partition(
896 &self,
897 _ctx: &QueryScanContext,
898 _metrics_set: &ExecutionPlanMetricsSet,
899 _partition: usize,
900 ) -> std::result::Result<SendableRecordBatchStream, BoxedError> {
901 Ok(Box::pin(EmptyRecordBatchStream::new(self.schema.clone())))
902 }
903
904 fn has_predicate_without_region(&self) -> bool {
905 true
906 }
907
908 fn add_dyn_filter_to_predicate(
909 &mut self,
910 filter_exprs: Vec<Arc<dyn PhysicalExpr>>,
911 ) -> Vec<bool> {
912 self.update_calls.fetch_add(1, Ordering::Relaxed);
913 self.last_filter_len
914 .store(filter_exprs.len(), Ordering::Relaxed);
915 vec![true; filter_exprs.len()]
916 }
917
918 fn set_logical_region(&mut self, logical_region: bool) {
919 self.properties.set_logical_region(logical_region);
920 }
921
922 fn set_query_load_region_id(&mut self, region_id: store_api::storage::RegionId) {
923 self.properties.set_query_load_region_id(region_id);
924 }
925 }
926
927 impl DisplayAs for RecordingScanner {
928 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929 write!(f, "RecordingScanner")
930 }
931 }
932
933 fn build_query_load_region_scan(
934 query_load_region_id: Option<RegionId>,
935 ) -> Arc<dyn ExecutionPlan> {
936 build_region_scan(query_load_region_id, None)
937 }
938
939 fn build_query_stat_counter_region_scan(
940 counters: RegionQueryStatCounters,
941 ) -> Arc<dyn ExecutionPlan> {
942 build_region_scan(None, Some(counters))
943 }
944
945 fn build_region_scan(
946 query_load_region_id: Option<RegionId>,
947 query_stat_counters: Option<RegionQueryStatCounters>,
948 ) -> Arc<dyn ExecutionPlan> {
949 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
950 "ts",
951 ConcreteDataType::timestamp_millisecond_datatype(),
952 false,
953 )]));
954
955 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
956 metadata_builder
957 .push_column_metadata(ColumnMetadata {
958 column_schema: ColumnSchema::new(
959 "ts",
960 ConcreteDataType::timestamp_millisecond_datatype(),
961 false,
962 )
963 .with_time_index(true),
964 semantic_type: SemanticType::Timestamp,
965 column_id: 1,
966 })
967 .primary_key(vec![]);
968 let metadata = Arc::new(metadata_builder.build().unwrap());
969 let mut scanner = RecordingScanner::new(
970 schema,
971 metadata,
972 Arc::new(AtomicUsize::new(0)),
973 Arc::new(AtomicUsize::new(0)),
974 );
975 if let Some(region_id) = query_load_region_id {
976 scanner.set_query_load_region_id(region_id);
977 }
978 if let Some(counters) = query_stat_counters {
979 scanner.properties.set_query_stat_counters(counters);
980 }
981
982 Arc::new(RegionScanExec::new(Box::new(scanner), ScanRequest::default(), None).unwrap())
983 }
984
985 fn query_stat_counters_for_test() -> RegionQueryStatCounters {
986 RegionQueryStatCounters {
987 query_cpu_time: Arc::new(AtomicU64::new(0)),
988 query_scanned_bytes: Arc::new(AtomicU64::new(0)),
989 }
990 }
991
992 #[test]
993 fn query_load_region_id_ignores_scans_without_region_id() {
994 let query_load_region_id = RegionId::new(1024, 42);
995 let scan_without_region_id = build_query_load_region_scan(None);
996 let scan_with_region_id = build_query_load_region_scan(Some(query_load_region_id));
997 let on: JoinOn = vec![(
998 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
999 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1000 )];
1001 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1002 HashJoinExec::try_new(
1003 scan_without_region_id,
1004 scan_with_region_id,
1005 on,
1006 None,
1007 &JoinType::Inner,
1008 None,
1009 PartitionMode::CollectLeft,
1010 NullEquality::NullEqualsNull,
1011 false,
1012 )
1013 .unwrap(),
1014 );
1015
1016 assert_eq!(
1017 super::query_load_region_id(&plan),
1018 Some(query_load_region_id.as_u64())
1019 );
1020 }
1021
1022 #[test]
1023 fn query_stat_counters_returns_shared_counter_for_multi_scan_plan() {
1024 let counters = query_stat_counters_for_test();
1025 let left = build_query_stat_counter_region_scan(counters.clone());
1026 let right = build_query_stat_counter_region_scan(counters.clone());
1027 let on: JoinOn = vec![(
1028 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1029 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1030 )];
1031 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1032 HashJoinExec::try_new(
1033 left,
1034 right,
1035 on,
1036 None,
1037 &JoinType::Inner,
1038 None,
1039 PartitionMode::CollectLeft,
1040 NullEquality::NullEqualsNull,
1041 false,
1042 )
1043 .unwrap(),
1044 );
1045
1046 let actual = super::query_stat_counters(&plan).unwrap();
1047 assert!(Arc::ptr_eq(
1048 &actual.query_cpu_time,
1049 &counters.query_cpu_time
1050 ));
1051 assert!(Arc::ptr_eq(
1052 &actual.query_scanned_bytes,
1053 &counters.query_scanned_bytes
1054 ));
1055 }
1056
1057 #[test]
1058 fn query_stat_counters_ignores_mixed_counter_plan() {
1059 let left = build_query_stat_counter_region_scan(query_stat_counters_for_test());
1060 let right = build_query_stat_counter_region_scan(query_stat_counters_for_test());
1061 let on: JoinOn = vec![(
1062 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1063 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1064 )];
1065 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1066 HashJoinExec::try_new(
1067 left,
1068 right,
1069 on,
1070 None,
1071 &JoinType::Inner,
1072 None,
1073 PartitionMode::CollectLeft,
1074 NullEquality::NullEqualsNull,
1075 false,
1076 )
1077 .unwrap(),
1078 );
1079
1080 assert!(super::query_stat_counters(&plan).is_none());
1081 }
1082
1083 async fn create_test_engine() -> QueryEngineRef {
1084 let catalog_manager = catalog::memory::new_memory_catalog_manager().unwrap();
1085 let req = RegisterTableRequest {
1086 catalog: DEFAULT_CATALOG_NAME.to_string(),
1087 schema: DEFAULT_SCHEMA_NAME.to_string(),
1088 table_name: NUMBERS_TABLE_NAME.to_string(),
1089 table_id: NUMBERS_TABLE_ID,
1090 table: NumbersTable::table(NUMBERS_TABLE_ID),
1091 };
1092 catalog_manager.register_table_sync(req).unwrap();
1093
1094 QueryEngineFactory::new(
1095 catalog_manager,
1096 None,
1097 None,
1098 None,
1099 None,
1100 false,
1101 QueryOptions::default(),
1102 )
1103 .query_engine()
1104 }
1105
1106 #[tokio::test]
1107 async fn test_sql_to_plan() {
1108 let engine = create_test_engine().await;
1109 let sql = "select sum(number) from numbers limit 20";
1110
1111 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1112 let plan = engine
1113 .planner()
1114 .plan(&stmt, QueryContext::arc())
1115 .await
1116 .unwrap();
1117
1118 assert_eq!(
1119 plan.to_string(),
1120 r#"Limit: skip=0, fetch=20
1121 Projection: sum(numbers.number)
1122 Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]
1123 TableScan: numbers"#
1124 );
1125 }
1126
1127 #[tokio::test]
1128 async fn test_purge_table_is_not_available_to_select() {
1129 let engine = create_test_engine().await;
1130 let stmt =
1131 QueryLanguageParser::parse_sql("select purge_table('numbers')", &QueryContext::arc())
1132 .unwrap();
1133
1134 assert!(
1135 engine
1136 .planner()
1137 .plan(&stmt, QueryContext::arc())
1138 .await
1139 .is_err()
1140 );
1141 }
1142
1143 #[tokio::test]
1144 async fn test_execute() {
1145 let engine = create_test_engine().await;
1146 let sql = "select sum(number) from numbers limit 20";
1147
1148 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1149 let plan = engine
1150 .planner()
1151 .plan(&stmt, QueryContext::arc())
1152 .await
1153 .unwrap();
1154
1155 let output = engine.execute(plan, QueryContext::arc()).await.unwrap();
1156
1157 match output.data {
1158 OutputData::Stream(recordbatch) => {
1159 let numbers = util::collect(recordbatch).await.unwrap();
1160 assert_eq!(1, numbers.len());
1161 assert_eq!(numbers[0].num_columns(), 1);
1162 assert_eq!(1, numbers[0].schema.num_columns());
1163 assert_eq!(
1164 "sum(numbers.number)",
1165 numbers[0].schema.column_schemas()[0].name
1166 );
1167
1168 let batch = &numbers[0];
1169 assert_eq!(1, batch.num_columns());
1170 assert_eq!(batch.column(0).len(), 1);
1171
1172 let expected = Arc::new(UInt64Array::from_iter_values([4950])) as ArrayRef;
1173 assert_eq!(batch.column(0), &expected);
1174 }
1175 _ => unreachable!(),
1176 }
1177 }
1178
1179 #[tokio::test]
1180 async fn test_read_table() {
1181 let engine = create_test_engine().await;
1182
1183 let engine = engine
1184 .as_any()
1185 .downcast_ref::<DatafusionQueryEngine>()
1186 .unwrap();
1187 let query_ctx = Arc::new(QueryContextBuilder::default().build());
1188 let table = engine
1189 .find_table(
1190 &ResolvedTableReference {
1191 catalog: "greptime".into(),
1192 schema: "public".into(),
1193 table: "numbers".into(),
1194 },
1195 &query_ctx,
1196 )
1197 .await
1198 .unwrap();
1199
1200 let df = engine.read_table(table).unwrap();
1201 let df = df
1202 .select_columns(&["number"])
1203 .unwrap()
1204 .filter(col("number").lt(lit(10)))
1205 .unwrap();
1206 let batches = df.collect().await.unwrap();
1207 assert_eq!(1, batches.len());
1208 let batch = &batches[0];
1209
1210 assert_eq!(1, batch.num_columns());
1211 assert_eq!(batch.column(0).len(), 10);
1212
1213 assert_eq!(
1214 Helper::try_into_vector(batch.column(0)).unwrap(),
1215 Arc::new(UInt32Vector::from_slice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) as VectorRef
1216 );
1217 }
1218
1219 #[tokio::test]
1220 async fn test_describe() {
1221 let engine = create_test_engine().await;
1222 let sql = "select sum(number) from numbers limit 20";
1223
1224 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1225
1226 let plan = engine
1227 .planner()
1228 .plan(&stmt, QueryContext::arc())
1229 .await
1230 .unwrap();
1231
1232 let DescribeResult { logical_plan } =
1233 engine.describe(plan, QueryContext::arc()).await.unwrap();
1234
1235 let schema: Schema = logical_plan.schema().clone().try_into().unwrap();
1236
1237 assert_eq!(
1238 schema.column_schemas()[0],
1239 ColumnSchema::new(
1240 "sum(numbers.number)",
1241 ConcreteDataType::uint64_datatype(),
1242 true
1243 )
1244 );
1245 assert_eq!(
1246 "Limit: skip=0, fetch=20\n Projection: sum(numbers.number)\n Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]\n TableScan: numbers",
1247 format!("{}", logical_plan.display_indent())
1248 );
1249 }
1250
1251 #[tokio::test]
1252 async fn test_topk_dynamic_filter_pushdown_reaches_region_scan() {
1253 let engine = create_test_engine().await;
1254 let engine = engine
1255 .as_any()
1256 .downcast_ref::<DatafusionQueryEngine>()
1257 .unwrap();
1258 let engine_ctx = engine.engine_context(QueryContext::arc());
1259 let state = engine_ctx.state();
1260
1261 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1262 "ts",
1263 ConcreteDataType::timestamp_millisecond_datatype(),
1264 false,
1265 )]));
1266
1267 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
1268 metadata_builder
1269 .push_column_metadata(ColumnMetadata {
1270 column_schema: ColumnSchema::new(
1271 "ts",
1272 ConcreteDataType::timestamp_millisecond_datatype(),
1273 false,
1274 )
1275 .with_time_index(true),
1276 semantic_type: SemanticType::Timestamp,
1277 column_id: 1,
1278 })
1279 .primary_key(vec![]);
1280 let metadata = Arc::new(metadata_builder.build().unwrap());
1281
1282 let update_calls = Arc::new(AtomicUsize::new(0));
1283 let last_filter_len = Arc::new(AtomicUsize::new(0));
1284 let scanner = Box::new(RecordingScanner::new(
1285 schema,
1286 metadata,
1287 update_calls.clone(),
1288 last_filter_len.clone(),
1289 ));
1290 let scan = Arc::new(RegionScanExec::new(scanner, ScanRequest::default(), None).unwrap());
1291
1292 let sort_expr = PhysicalSortExpr {
1293 expr: Arc::new(Column::new("ts", 0)),
1294 options: SortOptions {
1295 descending: true,
1296 ..Default::default()
1297 },
1298 };
1299 let partition_ranges: Vec<Vec<PartitionRange>> = vec![vec![]];
1300 let mut plan: Arc<dyn ExecutionPlan> =
1301 Arc::new(PartSortExec::try_new(sort_expr, Some(3), partition_ranges, scan).unwrap());
1302
1303 for optimizer in state.physical_optimizers() {
1304 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1305 }
1306
1307 assert!(update_calls.load(Ordering::Relaxed) > 0);
1308 assert!(last_filter_len.load(Ordering::Relaxed) > 0);
1309 }
1310
1311 #[tokio::test]
1312 async fn test_join_dynamic_filter_pushdown_reaches_region_scan() {
1313 let engine = create_test_engine().await;
1314 let engine = engine
1315 .as_any()
1316 .downcast_ref::<DatafusionQueryEngine>()
1317 .unwrap();
1318 let engine_ctx = engine.engine_context(QueryContext::arc());
1319 let state = engine_ctx.state();
1320
1321 assert!(
1322 state
1323 .config_options()
1324 .optimizer
1325 .enable_join_dynamic_filter_pushdown
1326 );
1327
1328 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1329 "ts",
1330 ConcreteDataType::timestamp_millisecond_datatype(),
1331 false,
1332 )]));
1333
1334 let mut left_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 1));
1335 left_metadata_builder
1336 .push_column_metadata(ColumnMetadata {
1337 column_schema: ColumnSchema::new(
1338 "ts",
1339 ConcreteDataType::timestamp_millisecond_datatype(),
1340 false,
1341 )
1342 .with_time_index(true),
1343 semantic_type: SemanticType::Timestamp,
1344 column_id: 1,
1345 })
1346 .primary_key(vec![]);
1347 let left_metadata = Arc::new(left_metadata_builder.build().unwrap());
1348
1349 let mut right_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 2));
1350 right_metadata_builder
1351 .push_column_metadata(ColumnMetadata {
1352 column_schema: ColumnSchema::new(
1353 "ts",
1354 ConcreteDataType::timestamp_millisecond_datatype(),
1355 false,
1356 )
1357 .with_time_index(true),
1358 semantic_type: SemanticType::Timestamp,
1359 column_id: 1,
1360 })
1361 .primary_key(vec![]);
1362 let right_metadata = Arc::new(right_metadata_builder.build().unwrap());
1363
1364 let left_update_calls = Arc::new(AtomicUsize::new(0));
1365 let left_last_filter_len = Arc::new(AtomicUsize::new(0));
1366 let right_update_calls = Arc::new(AtomicUsize::new(0));
1367 let right_last_filter_len = Arc::new(AtomicUsize::new(0));
1368
1369 let left_scan = Arc::new(
1370 RegionScanExec::new(
1371 Box::new(RecordingScanner::new(
1372 schema.clone(),
1373 left_metadata,
1374 left_update_calls.clone(),
1375 left_last_filter_len.clone(),
1376 )),
1377 ScanRequest::default(),
1378 None,
1379 )
1380 .unwrap(),
1381 );
1382 let right_scan = Arc::new(
1383 RegionScanExec::new(
1384 Box::new(RecordingScanner::new(
1385 schema,
1386 right_metadata,
1387 right_update_calls.clone(),
1388 right_last_filter_len.clone(),
1389 )),
1390 ScanRequest::default(),
1391 None,
1392 )
1393 .unwrap(),
1394 );
1395
1396 let on: JoinOn = vec![(
1397 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1398 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1399 )];
1400
1401 let mut plan: Arc<dyn ExecutionPlan> = Arc::new(
1402 HashJoinExec::try_new(
1403 left_scan,
1404 right_scan,
1405 on,
1406 None,
1407 &JoinType::Inner,
1408 None,
1409 PartitionMode::CollectLeft,
1410 NullEquality::NullEqualsNull,
1411 false,
1412 )
1413 .unwrap(),
1414 );
1415
1416 for optimizer in state.physical_optimizers() {
1417 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1418 }
1419
1420 assert!(left_update_calls.load(Ordering::Relaxed) > 0);
1421 assert_eq!(0, left_last_filter_len.load(Ordering::Relaxed));
1422 assert!(right_update_calls.load(Ordering::Relaxed) > 0);
1423 assert!(right_last_filter_len.load(Ordering::Relaxed) > 0);
1424 }
1425}