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_schema;
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;
33use common_recordbatch::{EmptyRecordBatchStream, 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 session::context::QueryContextRef;
48use snafu::{OptionExt, ResultExt, ensure};
49use sqlparser::ast::AnalyzeFormat;
50use table::TableRef;
51use table::requests::{DeleteRequest, InsertRequest};
52use table::table::scan::{REGION_SCAN_EXEC_NAME, RegionScanExec};
53use tracing::Span;
54
55use crate::analyze::DistAnalyzeExec;
56pub use crate::datafusion::planner::DfContextProviderAdapter;
57use crate::dist_plan::{
58 DistPlannerOptions, MergeScanLogicalPlan, RemoteDynFilterReceiverInjectorRef,
59};
60use crate::error::{
61 CatalogSnafu, CreateRecordBatchSnafu, MissingTableMutationHandlerSnafu,
62 MissingTimestampColumnSnafu, QueryExecutionSnafu, Result, TableMutationSnafu,
63 TableNotFoundSnafu, TableReadOnlySnafu, UnsupportedExprSnafu,
64};
65use crate::executor::QueryExecutor;
66use crate::metrics::{
67 OnDone, QUERY_STAGE_ELAPSED, maybe_attach_region_watermark_metrics,
68 should_collect_region_watermark_from_query_ctx,
69};
70use crate::options::ScheduledTimeExtension;
71use crate::physical_wrapper::PhysicalPlanWrapperRef;
72use crate::planner::{DfLogicalPlanner, LogicalPlanner};
73use crate::query_engine::{DescribeResult, QueryEngineContext, QueryEngineState};
74use crate::{QueryEngine, metrics};
75
76pub const QUERY_PARALLELISM_HINT: &str = "query_parallelism";
79
80pub const QUERY_FALLBACK_HINT: &str = "query_fallback";
82
83fn query_load_region_id(plan: &Arc<dyn ExecutionPlan>) -> Option<u64> {
84 let mut region_id = None;
85 let mut stack = vec![plan.clone()];
86
87 while let Some(plan) = stack.pop() {
88 if plan.name() == REGION_SCAN_EXEC_NAME
89 && let Some(scan) = plan.as_any().downcast_ref::<RegionScanExec>()
90 && let Some(scan_region_id) = scan.query_load_region_id()
91 {
92 match region_id {
93 Some(region_id) if region_id != scan_region_id => return None,
94 Some(_) => {}
95 None => region_id = Some(scan_region_id),
96 }
97 }
98 stack.extend(plan.children().into_iter().cloned());
99 }
100
101 region_id
102}
103
104pub struct DatafusionQueryEngine {
105 state: Arc<QueryEngineState>,
106 plugins: Plugins,
107}
108
109impl DatafusionQueryEngine {
110 pub fn new(state: Arc<QueryEngineState>, plugins: Plugins) -> Self {
111 Self { state, plugins }
112 }
113
114 #[tracing::instrument(skip_all)]
115 async fn exec_query_plan(
116 &self,
117 plan: LogicalPlan,
118 query_ctx: QueryContextRef,
119 ) -> Result<Output> {
120 let mut ctx = self.engine_context(query_ctx.clone());
121 let plan = if let Some(receiver_injector) =
122 self.plugins.get::<RemoteDynFilterReceiverInjectorRef>()
123 {
124 receiver_injector.maybe_inject(plan, query_ctx.clone())
125 } else {
126 plan
127 };
128
129 let physical_plan = self.create_physical_plan(&mut ctx, &plan).await?;
131 let physical_plan = self.optimize_physical_plan(&mut ctx, physical_plan)?;
132 let physical_plan = if let Some(wrapper) = self.plugins.get::<PhysicalPlanWrapperRef>() {
133 wrapper.wrap(physical_plan, query_ctx)
134 } else {
135 physical_plan
136 };
137
138 let stream = self.execute_stream(&ctx, &physical_plan)?;
139
140 Ok(Output::new(
141 OutputData::Stream(stream),
142 OutputMeta::new_with_plan(physical_plan),
143 ))
144 }
145
146 #[tracing::instrument(skip_all)]
147 async fn exec_dml_statement(
148 &self,
149 dml: DmlStatement,
150 query_ctx: QueryContextRef,
151 ) -> Result<Output> {
152 ensure!(
153 matches!(dml.op, WriteOp::Insert(_) | WriteOp::Delete),
154 UnsupportedExprSnafu {
155 name: format!("DML op {}", dml.op),
156 }
157 );
158
159 let _timer = QUERY_STAGE_ELAPSED
160 .with_label_values(&[dml.op.name()])
161 .start_timer();
162
163 let default_catalog = &query_ctx.current_catalog().to_owned();
164 let default_schema = &query_ctx.current_schema();
165 let table_name = dml.table_name.resolve(default_catalog, default_schema);
166 let table = self.find_table(&table_name, &query_ctx).await?;
167
168 let Output { data, meta } = self
169 .exec_query_plan((*dml.input).clone(), query_ctx.clone())
170 .await?;
171 let mut stream = match data {
172 OutputData::RecordBatches(batches) => batches.as_stream(),
173 OutputData::Stream(stream) => stream,
174 _ => unreachable!(),
175 };
176
177 let mut affected_rows = 0;
178 let mut insert_cost = 0;
179
180 while let Some(batch) = stream.next().await {
181 let batch = batch.context(CreateRecordBatchSnafu)?;
182 let column_vectors = batch
183 .column_vectors(&table_name.to_string(), table.schema())
184 .map_err(BoxedError::new)
185 .context(QueryExecutionSnafu)?;
186
187 match dml.op {
188 WriteOp::Insert(_) => {
189 let output = self
191 .insert(&table_name, column_vectors, query_ctx.clone())
192 .await?;
193 let (rows, cost) = output.extract_rows_and_cost();
194 affected_rows += rows;
195 insert_cost += cost;
196 }
197 WriteOp::Delete => {
198 affected_rows += self
199 .delete(&table_name, &table, column_vectors, query_ctx.clone())
200 .await?;
201 }
202 _ => unreachable!("guarded by the 'ensure!' at the beginning"),
203 }
204 }
205 Ok(Output::new(
206 OutputData::AffectedRows(affected_rows),
207 OutputMeta::new(meta.plan, insert_cost),
208 ))
209 }
210
211 #[tracing::instrument(skip_all)]
212 async fn delete(
213 &self,
214 table_name: &ResolvedTableReference,
215 table: &TableRef,
216 column_vectors: HashMap<String, VectorRef>,
217 query_ctx: QueryContextRef,
218 ) -> Result<usize> {
219 let catalog_name = table_name.catalog.to_string();
220 let schema_name = table_name.schema.to_string();
221 let table_name = table_name.table.to_string();
222 let table_schema = table.schema();
223
224 ensure!(
225 !is_readonly_schema(&schema_name),
226 TableReadOnlySnafu { table: table_name }
227 );
228
229 let ts_column = table_schema
230 .timestamp_column()
231 .map(|x| &x.name)
232 .with_context(|| MissingTimestampColumnSnafu {
233 table_name: table_name.clone(),
234 })?;
235
236 let table_info = table.table_info();
237 let rowkey_columns = table_info
238 .meta
239 .row_key_column_names()
240 .collect::<Vec<&String>>();
241 let column_vectors = column_vectors
242 .into_iter()
243 .filter(|x| &x.0 == ts_column || rowkey_columns.contains(&&x.0))
244 .collect::<HashMap<_, _>>();
245
246 let request = DeleteRequest {
247 catalog_name,
248 schema_name,
249 table_name,
250 key_column_values: column_vectors,
251 };
252
253 self.state
254 .table_mutation_handler()
255 .context(MissingTableMutationHandlerSnafu)?
256 .delete(request, query_ctx)
257 .await
258 .context(TableMutationSnafu)
259 }
260
261 #[tracing::instrument(skip_all)]
262 async fn insert(
263 &self,
264 table_name: &ResolvedTableReference,
265 column_vectors: HashMap<String, VectorRef>,
266 query_ctx: QueryContextRef,
267 ) -> Result<Output> {
268 let catalog_name = table_name.catalog.to_string();
269 let schema_name = table_name.schema.to_string();
270 let table_name = table_name.table.to_string();
271
272 ensure!(
273 !is_readonly_schema(&schema_name),
274 TableReadOnlySnafu { table: table_name }
275 );
276
277 let request = InsertRequest {
278 catalog_name,
279 schema_name,
280 table_name,
281 columns_values: column_vectors,
282 };
283
284 self.state
285 .table_mutation_handler()
286 .context(MissingTableMutationHandlerSnafu)?
287 .insert(request, query_ctx)
288 .await
289 .context(TableMutationSnafu)
290 }
291
292 async fn find_table(
293 &self,
294 table_name: &ResolvedTableReference,
295 query_context: &QueryContextRef,
296 ) -> Result<TableRef> {
297 let catalog_name = table_name.catalog.as_ref();
298 let schema_name = table_name.schema.as_ref();
299 let table_name = table_name.table.as_ref();
300
301 self.state
302 .catalog_manager()
303 .table(catalog_name, schema_name, table_name, Some(query_context))
304 .await
305 .context(CatalogSnafu)?
306 .with_context(|| TableNotFoundSnafu { table: table_name })
307 }
308
309 #[tracing::instrument(skip_all)]
310 async fn create_physical_plan(
311 &self,
312 ctx: &mut QueryEngineContext,
313 logical_plan: &LogicalPlan,
314 ) -> Result<Arc<dyn ExecutionPlan>> {
315 #[derive(Debug)]
319 struct PanicLogger<'a> {
320 input_logical_plan: &'a LogicalPlan,
321 after_analyze: Option<LogicalPlan>,
322 after_optimize: Option<LogicalPlan>,
323 phy_plan: Option<Arc<dyn ExecutionPlan>>,
324 }
325 impl Drop for PanicLogger<'_> {
326 fn drop(&mut self) {
327 if std::thread::panicking() {
328 common_telemetry::error!(
329 "Panic while creating physical plan, input logical plan: {:?}, after analyze: {:?}, after optimize: {:?}, final physical plan: {:?}",
330 self.input_logical_plan,
331 self.after_analyze,
332 self.after_optimize,
333 self.phy_plan
334 );
335 }
336 }
337 }
338
339 let mut logger = PanicLogger {
340 input_logical_plan: logical_plan,
341 after_analyze: None,
342 after_optimize: None,
343 phy_plan: None,
344 };
345
346 let _timer = metrics::CREATE_PHYSICAL_ELAPSED.start_timer();
347 let state = ctx.state();
348
349 common_telemetry::debug!("Create physical plan, input plan: {logical_plan}");
350
351 if matches!(logical_plan, DfLogicalPlan::Explain(_)) {
353 return state
354 .create_physical_plan(logical_plan)
355 .await
356 .map_err(Into::into);
357 }
358
359 let analyzed_plan = state.analyzer().execute_and_check(
361 logical_plan.clone(),
362 state.config_options(),
363 |_, _| {},
364 )?;
365
366 logger.after_analyze = Some(analyzed_plan.clone());
367
368 common_telemetry::debug!("Create physical plan, analyzed plan: {analyzed_plan}");
369
370 let optimized_plan = if let DfLogicalPlan::Extension(ext) = &analyzed_plan
372 && ext.node.name() == MergeScanLogicalPlan::name()
373 {
374 analyzed_plan.clone()
375 } else {
376 state
377 .optimizer()
378 .optimize(analyzed_plan, state, |_, _| {})?
379 };
380
381 common_telemetry::debug!("Create physical plan, optimized plan: {optimized_plan}");
382 logger.after_optimize = Some(optimized_plan.clone());
383
384 let physical_plan = state
385 .query_planner()
386 .create_physical_plan(&optimized_plan, state)
387 .await?;
388
389 logger.phy_plan = Some(physical_plan.clone());
390 drop(logger);
391 Ok(physical_plan)
392 }
393
394 #[tracing::instrument(skip_all)]
395 fn optimize_physical_plan(
396 &self,
397 ctx: &mut QueryEngineContext,
398 plan: Arc<dyn ExecutionPlan>,
399 ) -> Result<Arc<dyn ExecutionPlan>> {
400 let _timer = metrics::OPTIMIZE_PHYSICAL_ELAPSED.start_timer();
401
402 let optimized_plan = if let Some(analyze_plan) = plan.as_any().downcast_ref::<AnalyzeExec>()
409 {
410 let format = if let Some(format) = ctx.query_ctx().explain_format()
411 && format.to_lowercase() == "json"
412 {
413 AnalyzeFormat::JSON
414 } else {
415 AnalyzeFormat::TEXT
416 };
417 ctx.query_ctx().set_explain_verbose(analyze_plan.verbose());
420
421 Arc::new(DistAnalyzeExec::new(
422 analyze_plan.input().clone(),
423 analyze_plan.verbose(),
424 format,
425 ))
426 } else {
434 plan
435 };
443
444 Ok(optimized_plan)
445 }
446}
447
448#[async_trait]
449impl QueryEngine for DatafusionQueryEngine {
450 fn as_any(&self) -> &dyn Any {
451 self
452 }
453
454 fn planner(&self) -> Arc<dyn LogicalPlanner> {
455 Arc::new(DfLogicalPlanner::new(self.state.clone()))
456 }
457
458 fn name(&self) -> &str {
459 "datafusion"
460 }
461
462 async fn describe(
463 &self,
464 plan: LogicalPlan,
465 _query_ctx: QueryContextRef,
466 ) -> Result<DescribeResult> {
467 Ok(DescribeResult { logical_plan: plan })
468 }
469
470 async fn execute(&self, plan: LogicalPlan, query_ctx: QueryContextRef) -> Result<Output> {
471 match plan {
472 LogicalPlan::Dml(dml) => self.exec_dml_statement(dml, query_ctx).await,
473 _ => self.exec_query_plan(plan, query_ctx).await,
474 }
475 }
476
477 fn register_aggregate_function(&self, func: AggregateUDF) {
485 self.state.register_aggr_function(func);
486 }
487
488 fn register_scalar_function(&self, func: ScalarFunctionFactory) {
491 self.state.register_scalar_function(func);
492 }
493
494 fn register_table_function(&self, func: Arc<TableFunction>) {
495 self.state.register_table_function(func);
496 }
497
498 fn register_window_function(&self, func: WindowUDF) {
499 self.state.register_window_function(func);
500 }
501
502 fn read_table(&self, table: TableRef) -> Result<DataFrame> {
503 self.state.read_table(table).map_err(Into::into)
504 }
505
506 fn engine_context(&self, query_ctx: QueryContextRef) -> QueryEngineContext {
507 let mut state = self.state.session_state();
508 state.config_mut().set_extension(query_ctx.clone());
509 state.config_mut().set_extension(self.state.clone());
510 if let Some(parallelism) = query_ctx.extension(QUERY_PARALLELISM_HINT) {
513 if let Ok(n) = parallelism.parse::<u64>() {
514 if n > 0 {
515 let new_cfg = state.config().clone().with_target_partitions(n as usize);
516 *state.config_mut() = new_cfg;
517 }
518 } else {
519 common_telemetry::warn!(
520 "Failed to parse query_parallelism: {}, using default value",
521 parallelism
522 );
523 }
524 }
525
526 state.config_mut().options_mut().execution.time_zone =
528 Some(query_ctx.timezone().to_string());
529
530 if query_ctx.configuration_parameter().allow_query_fallback() {
533 state
534 .config_mut()
535 .options_mut()
536 .extensions
537 .insert(DistPlannerOptions {
538 allow_query_fallback: true,
539 });
540 } else if let Some(fallback) = query_ctx.extension(QUERY_FALLBACK_HINT) {
541 if fallback.to_lowercase().parse::<bool>().unwrap_or(false) {
544 state
545 .config_mut()
546 .options_mut()
547 .extensions
548 .insert(DistPlannerOptions {
549 allow_query_fallback: true,
550 });
551 }
552 }
553
554 state
555 .config_mut()
556 .options_mut()
557 .extensions
558 .insert(FunctionContext {
559 query_ctx: query_ctx.clone(),
560 state: self.engine_state().function_state(),
561 });
562
563 state
567 .config_mut()
568 .options_mut()
569 .extensions
570 .insert(ScheduledTimeExtension {
571 scheduled_time: crate::options::scheduled_time_from_ctx(&query_ctx),
572 });
573
574 let config_options = state.config_options().clone();
575 let _ = state
576 .execution_props_mut()
577 .config_options
578 .insert(config_options);
579
580 match crate::options::parse_scheduled_time_datetime(&query_ctx.extensions()) {
584 Ok(Some(scheduled_rt)) => {
585 state.execution_props_mut().query_execution_start_time = Some(scheduled_rt);
586 }
587 Ok(None) => {}
588 Err(err) => {
589 common_telemetry::warn!(err; "Ignoring invalid scheduled time query extension");
590 }
591 }
592
593 QueryEngineContext::new(state, query_ctx)
594 }
595
596 fn engine_state(&self) -> &QueryEngineState {
597 &self.state
598 }
599}
600
601impl QueryExecutor for DatafusionQueryEngine {
602 #[tracing::instrument(skip_all)]
603 fn execute_stream(
604 &self,
605 ctx: &QueryEngineContext,
606 plan: &Arc<dyn ExecutionPlan>,
607 ) -> Result<SendableRecordBatchStream> {
608 let query_ctx = ctx.query_ctx();
609 let explain_verbose = query_ctx.explain_verbose();
610 let should_collect_region_watermark =
611 should_collect_region_watermark_from_query_ctx(&query_ctx)?;
612 let output_partitions = plan.properties().output_partitioning().partition_count();
613 if explain_verbose {
614 common_telemetry::info!("Executing query plan, output_partitions: {output_partitions}");
615 }
616
617 let exec_timer = metrics::EXEC_PLAN_ELAPSED.start_timer();
618 let task_ctx = ctx.build_task_ctx();
619 let span = Span::current();
620
621 match plan.properties().output_partitioning().partition_count() {
622 0 => {
623 let schema = Arc::new(
624 Schema::try_from(plan.schema())
625 .map_err(BoxedError::new)
626 .context(QueryExecutionSnafu)?,
627 );
628 Ok(Box::pin(EmptyRecordBatchStream::new(schema)))
629 }
630 1 => {
631 let df_stream = plan.execute(0, task_ctx)?;
632 let mut stream = RecordBatchStreamAdapter::try_new_with_span(df_stream, span)
633 .context(error::ConvertDfRecordBatchStreamSnafu)
634 .map_err(BoxedError::new)
635 .context(QueryExecutionSnafu)?;
636 stream.set_metrics2(plan.clone());
637 stream.set_query_load_region_id(query_load_region_id(plan));
638 stream.set_explain_verbose(explain_verbose);
639 let stream = OnDone::new(Box::pin(stream), move || {
640 let exec_cost = exec_timer.stop_and_record();
641 if explain_verbose {
642 common_telemetry::info!(
643 "DatafusionQueryEngine execute 1 stream, cost: {:?}s",
644 exec_cost,
645 );
646 }
647 });
648 Ok(maybe_attach_region_watermark_metrics(
649 Box::pin(stream),
650 plan.clone(),
651 should_collect_region_watermark,
652 ))
653 }
654 _ => {
655 let merged_plan = CoalescePartitionsExec::new(plan.clone());
657 assert_eq!(
659 1,
660 merged_plan
661 .properties()
662 .output_partitioning()
663 .partition_count()
664 );
665 let df_stream = merged_plan.execute(0, task_ctx)?;
666 let mut stream = RecordBatchStreamAdapter::try_new_with_span(df_stream, span)
667 .context(error::ConvertDfRecordBatchStreamSnafu)
668 .map_err(BoxedError::new)
669 .context(QueryExecutionSnafu)?;
670 stream.set_metrics2(plan.clone());
671 stream.set_query_load_region_id(query_load_region_id(plan));
672 stream.set_explain_verbose(explain_verbose);
673 let stream = OnDone::new(Box::pin(stream), move || {
674 let exec_cost = exec_timer.stop_and_record();
675 if explain_verbose {
676 common_telemetry::info!(
677 "DatafusionQueryEngine execute {output_partitions} stream, cost: {:?}s",
678 exec_cost
679 );
680 }
681 });
682 Ok(maybe_attach_region_watermark_metrics(
683 Box::pin(stream),
684 plan.clone(),
685 should_collect_region_watermark,
686 ))
687 }
688 }
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use std::fmt;
695 use std::sync::Arc;
696 use std::sync::atomic::{AtomicUsize, Ordering};
697
698 use api::v1::SemanticType;
699 use arrow::array::{ArrayRef, UInt64Array};
700 use arrow_schema::SortOptions;
701 use catalog::RegisterTableRequest;
702 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, NUMBERS_TABLE_ID};
703 use common_error::ext::BoxedError;
704 use common_recordbatch::{EmptyRecordBatchStream, SendableRecordBatchStream, util};
705 use datafusion::physical_plan::display::{DisplayAs, DisplayFormatType};
706 use datafusion::physical_plan::expressions::PhysicalSortExpr;
707 use datafusion::physical_plan::joins::{HashJoinExec, JoinOn, PartitionMode};
708 use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet;
709 use datafusion::physical_plan::{ExecutionPlan, PhysicalExpr};
710 use datafusion::prelude::{col, lit};
711 use datafusion_common::{JoinType, NullEquality};
712 use datafusion_physical_expr::expressions::Column;
713 use datatypes::prelude::ConcreteDataType;
714 use datatypes::schema::{ColumnSchema, SchemaRef};
715 use datatypes::vectors::{Helper, UInt32Vector, VectorRef};
716 use session::context::{QueryContext, QueryContextBuilder};
717 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder, RegionMetadataRef};
718 use store_api::region_engine::{
719 PartitionRange, PrepareRequest, QueryScanContext, RegionScanner, ScannerProperties,
720 };
721 use store_api::storage::{RegionId, ScanRequest};
722 use table::table::numbers::{NUMBERS_TABLE_NAME, NumbersTable};
723 use table::table::scan::RegionScanExec;
724
725 use super::*;
726 use crate::options::QueryOptions;
727 use crate::parser::QueryLanguageParser;
728 use crate::part_sort::PartSortExec;
729 use crate::query_engine::{QueryEngineFactory, QueryEngineRef};
730
731 #[derive(Debug)]
732 struct RecordingScanner {
733 schema: SchemaRef,
734 metadata: RegionMetadataRef,
735 properties: ScannerProperties,
736 update_calls: Arc<AtomicUsize>,
737 last_filter_len: Arc<AtomicUsize>,
738 }
739
740 impl RecordingScanner {
741 fn new(
742 schema: SchemaRef,
743 metadata: RegionMetadataRef,
744 update_calls: Arc<AtomicUsize>,
745 last_filter_len: Arc<AtomicUsize>,
746 ) -> Self {
747 Self {
748 schema,
749 metadata,
750 properties: ScannerProperties::default(),
751 update_calls,
752 last_filter_len,
753 }
754 }
755 }
756
757 impl RegionScanner for RecordingScanner {
758 fn name(&self) -> &str {
759 "RecordingScanner"
760 }
761
762 fn properties(&self) -> &ScannerProperties {
763 &self.properties
764 }
765
766 fn schema(&self) -> SchemaRef {
767 self.schema.clone()
768 }
769
770 fn metadata(&self) -> RegionMetadataRef {
771 self.metadata.clone()
772 }
773
774 fn prepare(&mut self, request: PrepareRequest) -> std::result::Result<(), BoxedError> {
775 self.properties.prepare(request);
776 Ok(())
777 }
778
779 fn scan_partition(
780 &self,
781 _ctx: &QueryScanContext,
782 _metrics_set: &ExecutionPlanMetricsSet,
783 _partition: usize,
784 ) -> std::result::Result<SendableRecordBatchStream, BoxedError> {
785 Ok(Box::pin(EmptyRecordBatchStream::new(self.schema.clone())))
786 }
787
788 fn has_predicate_without_region(&self) -> bool {
789 true
790 }
791
792 fn add_dyn_filter_to_predicate(
793 &mut self,
794 filter_exprs: Vec<Arc<dyn PhysicalExpr>>,
795 ) -> Vec<bool> {
796 self.update_calls.fetch_add(1, Ordering::Relaxed);
797 self.last_filter_len
798 .store(filter_exprs.len(), Ordering::Relaxed);
799 vec![true; filter_exprs.len()]
800 }
801
802 fn set_logical_region(&mut self, logical_region: bool) {
803 self.properties.set_logical_region(logical_region);
804 }
805
806 fn set_query_load_region_id(&mut self, region_id: store_api::storage::RegionId) {
807 self.properties.set_query_load_region_id(region_id);
808 }
809 }
810
811 impl DisplayAs for RecordingScanner {
812 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 write!(f, "RecordingScanner")
814 }
815 }
816
817 fn build_query_load_region_scan(
818 query_load_region_id: Option<RegionId>,
819 ) -> Arc<dyn ExecutionPlan> {
820 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
821 "ts",
822 ConcreteDataType::timestamp_millisecond_datatype(),
823 false,
824 )]));
825
826 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
827 metadata_builder
828 .push_column_metadata(ColumnMetadata {
829 column_schema: ColumnSchema::new(
830 "ts",
831 ConcreteDataType::timestamp_millisecond_datatype(),
832 false,
833 )
834 .with_time_index(true),
835 semantic_type: SemanticType::Timestamp,
836 column_id: 1,
837 })
838 .primary_key(vec![]);
839 let metadata = Arc::new(metadata_builder.build().unwrap());
840 let mut scanner = RecordingScanner::new(
841 schema,
842 metadata,
843 Arc::new(AtomicUsize::new(0)),
844 Arc::new(AtomicUsize::new(0)),
845 );
846 if let Some(region_id) = query_load_region_id {
847 scanner.set_query_load_region_id(region_id);
848 }
849
850 Arc::new(RegionScanExec::new(Box::new(scanner), ScanRequest::default(), None).unwrap())
851 }
852
853 #[test]
854 fn query_load_region_id_ignores_scans_without_region_id() {
855 let query_load_region_id = RegionId::new(1024, 42);
856 let scan_without_region_id = build_query_load_region_scan(None);
857 let scan_with_region_id = build_query_load_region_scan(Some(query_load_region_id));
858 let on: JoinOn = vec![(
859 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
860 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
861 )];
862 let plan: Arc<dyn ExecutionPlan> = Arc::new(
863 HashJoinExec::try_new(
864 scan_without_region_id,
865 scan_with_region_id,
866 on,
867 None,
868 &JoinType::Inner,
869 None,
870 PartitionMode::CollectLeft,
871 NullEquality::NullEqualsNull,
872 false,
873 )
874 .unwrap(),
875 );
876
877 assert_eq!(
878 super::query_load_region_id(&plan),
879 Some(query_load_region_id.as_u64())
880 );
881 }
882
883 async fn create_test_engine() -> QueryEngineRef {
884 let catalog_manager = catalog::memory::new_memory_catalog_manager().unwrap();
885 let req = RegisterTableRequest {
886 catalog: DEFAULT_CATALOG_NAME.to_string(),
887 schema: DEFAULT_SCHEMA_NAME.to_string(),
888 table_name: NUMBERS_TABLE_NAME.to_string(),
889 table_id: NUMBERS_TABLE_ID,
890 table: NumbersTable::table(NUMBERS_TABLE_ID),
891 };
892 catalog_manager.register_table_sync(req).unwrap();
893
894 QueryEngineFactory::new(
895 catalog_manager,
896 None,
897 None,
898 None,
899 None,
900 false,
901 QueryOptions::default(),
902 )
903 .query_engine()
904 }
905
906 #[tokio::test]
907 async fn test_sql_to_plan() {
908 let engine = create_test_engine().await;
909 let sql = "select sum(number) from numbers limit 20";
910
911 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
912 let plan = engine
913 .planner()
914 .plan(&stmt, QueryContext::arc())
915 .await
916 .unwrap();
917
918 assert_eq!(
919 plan.to_string(),
920 r#"Limit: skip=0, fetch=20
921 Projection: sum(numbers.number)
922 Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]
923 TableScan: numbers"#
924 );
925 }
926
927 #[tokio::test]
928 async fn test_execute() {
929 let engine = create_test_engine().await;
930 let sql = "select sum(number) from numbers limit 20";
931
932 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
933 let plan = engine
934 .planner()
935 .plan(&stmt, QueryContext::arc())
936 .await
937 .unwrap();
938
939 let output = engine.execute(plan, QueryContext::arc()).await.unwrap();
940
941 match output.data {
942 OutputData::Stream(recordbatch) => {
943 let numbers = util::collect(recordbatch).await.unwrap();
944 assert_eq!(1, numbers.len());
945 assert_eq!(numbers[0].num_columns(), 1);
946 assert_eq!(1, numbers[0].schema.num_columns());
947 assert_eq!(
948 "sum(numbers.number)",
949 numbers[0].schema.column_schemas()[0].name
950 );
951
952 let batch = &numbers[0];
953 assert_eq!(1, batch.num_columns());
954 assert_eq!(batch.column(0).len(), 1);
955
956 let expected = Arc::new(UInt64Array::from_iter_values([4950])) as ArrayRef;
957 assert_eq!(batch.column(0), &expected);
958 }
959 _ => unreachable!(),
960 }
961 }
962
963 #[tokio::test]
964 async fn test_read_table() {
965 let engine = create_test_engine().await;
966
967 let engine = engine
968 .as_any()
969 .downcast_ref::<DatafusionQueryEngine>()
970 .unwrap();
971 let query_ctx = Arc::new(QueryContextBuilder::default().build());
972 let table = engine
973 .find_table(
974 &ResolvedTableReference {
975 catalog: "greptime".into(),
976 schema: "public".into(),
977 table: "numbers".into(),
978 },
979 &query_ctx,
980 )
981 .await
982 .unwrap();
983
984 let df = engine.read_table(table).unwrap();
985 let df = df
986 .select_columns(&["number"])
987 .unwrap()
988 .filter(col("number").lt(lit(10)))
989 .unwrap();
990 let batches = df.collect().await.unwrap();
991 assert_eq!(1, batches.len());
992 let batch = &batches[0];
993
994 assert_eq!(1, batch.num_columns());
995 assert_eq!(batch.column(0).len(), 10);
996
997 assert_eq!(
998 Helper::try_into_vector(batch.column(0)).unwrap(),
999 Arc::new(UInt32Vector::from_slice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) as VectorRef
1000 );
1001 }
1002
1003 #[tokio::test]
1004 async fn test_describe() {
1005 let engine = create_test_engine().await;
1006 let sql = "select sum(number) from numbers limit 20";
1007
1008 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1009
1010 let plan = engine
1011 .planner()
1012 .plan(&stmt, QueryContext::arc())
1013 .await
1014 .unwrap();
1015
1016 let DescribeResult { logical_plan } =
1017 engine.describe(plan, QueryContext::arc()).await.unwrap();
1018
1019 let schema: Schema = logical_plan.schema().clone().try_into().unwrap();
1020
1021 assert_eq!(
1022 schema.column_schemas()[0],
1023 ColumnSchema::new(
1024 "sum(numbers.number)",
1025 ConcreteDataType::uint64_datatype(),
1026 true
1027 )
1028 );
1029 assert_eq!(
1030 "Limit: skip=0, fetch=20\n Projection: sum(numbers.number)\n Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]\n TableScan: numbers",
1031 format!("{}", logical_plan.display_indent())
1032 );
1033 }
1034
1035 #[tokio::test]
1036 async fn test_topk_dynamic_filter_pushdown_reaches_region_scan() {
1037 let engine = create_test_engine().await;
1038 let engine = engine
1039 .as_any()
1040 .downcast_ref::<DatafusionQueryEngine>()
1041 .unwrap();
1042 let engine_ctx = engine.engine_context(QueryContext::arc());
1043 let state = engine_ctx.state();
1044
1045 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1046 "ts",
1047 ConcreteDataType::timestamp_millisecond_datatype(),
1048 false,
1049 )]));
1050
1051 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
1052 metadata_builder
1053 .push_column_metadata(ColumnMetadata {
1054 column_schema: ColumnSchema::new(
1055 "ts",
1056 ConcreteDataType::timestamp_millisecond_datatype(),
1057 false,
1058 )
1059 .with_time_index(true),
1060 semantic_type: SemanticType::Timestamp,
1061 column_id: 1,
1062 })
1063 .primary_key(vec![]);
1064 let metadata = Arc::new(metadata_builder.build().unwrap());
1065
1066 let update_calls = Arc::new(AtomicUsize::new(0));
1067 let last_filter_len = Arc::new(AtomicUsize::new(0));
1068 let scanner = Box::new(RecordingScanner::new(
1069 schema,
1070 metadata,
1071 update_calls.clone(),
1072 last_filter_len.clone(),
1073 ));
1074 let scan = Arc::new(RegionScanExec::new(scanner, ScanRequest::default(), None).unwrap());
1075
1076 let sort_expr = PhysicalSortExpr {
1077 expr: Arc::new(Column::new("ts", 0)),
1078 options: SortOptions {
1079 descending: true,
1080 ..Default::default()
1081 },
1082 };
1083 let partition_ranges: Vec<Vec<PartitionRange>> = vec![vec![]];
1084 let mut plan: Arc<dyn ExecutionPlan> =
1085 Arc::new(PartSortExec::try_new(sort_expr, Some(3), partition_ranges, scan).unwrap());
1086
1087 for optimizer in state.physical_optimizers() {
1088 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1089 }
1090
1091 assert!(update_calls.load(Ordering::Relaxed) > 0);
1092 assert!(last_filter_len.load(Ordering::Relaxed) > 0);
1093 }
1094
1095 #[tokio::test]
1096 async fn test_join_dynamic_filter_pushdown_reaches_region_scan() {
1097 let engine = create_test_engine().await;
1098 let engine = engine
1099 .as_any()
1100 .downcast_ref::<DatafusionQueryEngine>()
1101 .unwrap();
1102 let engine_ctx = engine.engine_context(QueryContext::arc());
1103 let state = engine_ctx.state();
1104
1105 assert!(
1106 state
1107 .config_options()
1108 .optimizer
1109 .enable_join_dynamic_filter_pushdown
1110 );
1111
1112 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1113 "ts",
1114 ConcreteDataType::timestamp_millisecond_datatype(),
1115 false,
1116 )]));
1117
1118 let mut left_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 1));
1119 left_metadata_builder
1120 .push_column_metadata(ColumnMetadata {
1121 column_schema: ColumnSchema::new(
1122 "ts",
1123 ConcreteDataType::timestamp_millisecond_datatype(),
1124 false,
1125 )
1126 .with_time_index(true),
1127 semantic_type: SemanticType::Timestamp,
1128 column_id: 1,
1129 })
1130 .primary_key(vec![]);
1131 let left_metadata = Arc::new(left_metadata_builder.build().unwrap());
1132
1133 let mut right_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 2));
1134 right_metadata_builder
1135 .push_column_metadata(ColumnMetadata {
1136 column_schema: ColumnSchema::new(
1137 "ts",
1138 ConcreteDataType::timestamp_millisecond_datatype(),
1139 false,
1140 )
1141 .with_time_index(true),
1142 semantic_type: SemanticType::Timestamp,
1143 column_id: 1,
1144 })
1145 .primary_key(vec![]);
1146 let right_metadata = Arc::new(right_metadata_builder.build().unwrap());
1147
1148 let left_update_calls = Arc::new(AtomicUsize::new(0));
1149 let left_last_filter_len = Arc::new(AtomicUsize::new(0));
1150 let right_update_calls = Arc::new(AtomicUsize::new(0));
1151 let right_last_filter_len = Arc::new(AtomicUsize::new(0));
1152
1153 let left_scan = Arc::new(
1154 RegionScanExec::new(
1155 Box::new(RecordingScanner::new(
1156 schema.clone(),
1157 left_metadata,
1158 left_update_calls.clone(),
1159 left_last_filter_len.clone(),
1160 )),
1161 ScanRequest::default(),
1162 None,
1163 )
1164 .unwrap(),
1165 );
1166 let right_scan = Arc::new(
1167 RegionScanExec::new(
1168 Box::new(RecordingScanner::new(
1169 schema,
1170 right_metadata,
1171 right_update_calls.clone(),
1172 right_last_filter_len.clone(),
1173 )),
1174 ScanRequest::default(),
1175 None,
1176 )
1177 .unwrap(),
1178 );
1179
1180 let on: JoinOn = vec![(
1181 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1182 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1183 )];
1184
1185 let mut plan: Arc<dyn ExecutionPlan> = Arc::new(
1186 HashJoinExec::try_new(
1187 left_scan,
1188 right_scan,
1189 on,
1190 None,
1191 &JoinType::Inner,
1192 None,
1193 PartitionMode::CollectLeft,
1194 NullEquality::NullEqualsNull,
1195 false,
1196 )
1197 .unwrap(),
1198 );
1199
1200 for optimizer in state.physical_optimizers() {
1201 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1202 }
1203
1204 assert!(left_update_calls.load(Ordering::Relaxed) > 0);
1205 assert_eq!(0, left_last_filter_len.load(Ordering::Relaxed));
1206 assert!(right_update_calls.load(Ordering::Relaxed) > 0);
1207 assert!(right_last_filter_len.load(Ordering::Relaxed) > 0);
1208 }
1209}