1mod error;
18mod json_expr_planner;
19mod pg_oid_alias_expr_planner;
20mod planner;
21
22use std::any::Any;
23use std::collections::HashMap;
24use std::sync::Arc;
25
26use async_trait::async_trait;
27use common_base::Plugins;
28use common_catalog::consts::is_readonly_table;
29use common_error::ext::BoxedError;
30use common_function::function::FunctionContext;
31use common_function::function_factory::ScalarFunctionFactory;
32use common_query::{Output, OutputData, OutputMeta};
33use common_recordbatch::adapter::{RecordBatchStreamAdapter, RegionQueryStatCounters};
34use common_recordbatch::{EmptyRecordBatchStream, RecordBatch, SendableRecordBatchStream};
35use common_telemetry::tracing;
36use datafusion::catalog::TableFunction;
37use datafusion::dataframe::DataFrame;
38use datafusion::physical_plan::ExecutionPlan;
39use datafusion::physical_plan::analyze::AnalyzeExec;
40use datafusion::physical_plan::coalesce_partitions::CoalescePartitionsExec;
41use datafusion_common::ResolvedTableReference;
42use datafusion_expr::{
43 AggregateUDF, DmlStatement, LogicalPlan as DfLogicalPlan, LogicalPlan, WindowUDF, WriteOp,
44};
45use datatypes::prelude::VectorRef;
46use datatypes::schema::Schema;
47use futures_util::StreamExt;
48use futures_util::future::try_join;
49use session::context::QueryContextRef;
50use snafu::{OptionExt, ResultExt, ensure};
51use sqlparser::ast::AnalyzeFormat;
52use table::TableRef;
53use table::requests::{DeleteRequest, InsertRequest};
54use table::table::scan::{REGION_SCAN_EXEC_NAME, RegionScanExec};
55use tracing::Span;
56
57use crate::analyze::DistAnalyzeExec;
58pub use crate::datafusion::planner::DfContextProviderAdapter;
59use crate::dist_plan::{
60 DistPlannerOptions, MergeScanLogicalPlan, RemoteDynFilterReceiverInjectorRef,
61};
62use crate::error::{
63 CatalogSnafu, CreateRecordBatchSnafu, MissingTableMutationHandlerSnafu,
64 MissingTimestampColumnSnafu, QueryExecutionSnafu, Result, TableMutationSnafu,
65 TableNotFoundSnafu, TableReadOnlySnafu, UnsupportedExprSnafu,
66};
67use crate::executor::QueryExecutor;
68use crate::metrics::{
69 OnDone, QUERY_STAGE_ELAPSED, maybe_attach_region_watermark_metrics,
70 should_collect_region_watermark_from_query_ctx,
71};
72use crate::options::ScheduledTimeExtension;
73use crate::physical_wrapper::PhysicalPlanWrapperRef;
74use crate::planner::{DfLogicalPlanner, LogicalPlanner};
75use crate::query_engine::{DescribeResult, QueryEngineContext, QueryEngineState};
76use crate::{QueryEngine, metrics};
77
78pub const QUERY_PARALLELISM_HINT: &str = "query_parallelism";
81
82pub const QUERY_FALLBACK_HINT: &str = "query_fallback";
84
85async fn forward_record_batches(
88 mut stream: SendableRecordBatchStream,
89 batch_tx: tokio::sync::mpsc::UnboundedSender<Result<RecordBatch>>,
90) -> Result<()> {
91 while let Some(batch) = stream.next().await {
92 match batch.context(CreateRecordBatchSnafu) {
93 Ok(batch) => {
94 if batch_tx.send(Ok(batch)).is_err() {
95 break;
96 }
97 tokio::task::yield_now().await;
98 }
99 Err(error) => {
100 let _ = batch_tx.send(Err(error));
101 break;
102 }
103 }
104 }
105 Ok(())
106}
107
108fn query_load_region_id(plan: &Arc<dyn ExecutionPlan>) -> Option<u64> {
109 let mut region_id = None;
110 let mut stack = vec![plan.clone()];
111
112 while let Some(plan) = stack.pop() {
113 if plan.name() == REGION_SCAN_EXEC_NAME
114 && let Some(scan) = plan.downcast_ref::<RegionScanExec>()
115 && let Some(scan_region_id) = scan.query_load_region_id()
116 {
117 match region_id {
118 Some(region_id) if region_id != scan_region_id => return None,
119 Some(_) => {}
120 None => region_id = Some(scan_region_id),
121 }
122 }
123 stack.extend(plan.children().into_iter().cloned());
124 }
125
126 region_id
127}
128
129fn query_stat_counters(plan: &Arc<dyn ExecutionPlan>) -> Option<RegionQueryStatCounters> {
138 let mut counters: Option<RegionQueryStatCounters> = None;
139 let mut stack = vec![plan.clone()];
140
141 while let Some(plan) = stack.pop() {
142 if plan.name() == REGION_SCAN_EXEC_NAME
143 && let Some(scan) = plan.downcast_ref::<RegionScanExec>()
144 && let Some(scan_counters) = scan.query_stat_counters()
145 {
146 match &counters {
147 Some(counters)
148 if !Arc::ptr_eq(&counters.query_cpu_time, &scan_counters.query_cpu_time)
149 || !Arc::ptr_eq(
150 &counters.query_scanned_bytes,
151 &scan_counters.query_scanned_bytes,
152 ) =>
153 {
154 return None;
155 }
156 Some(_) => {}
157 None => counters = Some(scan_counters),
158 }
159 }
160 stack.extend(plan.children().into_iter().cloned());
161 }
162
163 counters
164}
165
166pub struct DatafusionQueryEngine {
167 state: Arc<QueryEngineState>,
168 plugins: Plugins,
169}
170
171impl DatafusionQueryEngine {
172 pub fn new(state: Arc<QueryEngineState>, plugins: Plugins) -> Self {
173 Self { state, plugins }
174 }
175
176 #[tracing::instrument(skip_all)]
177 async fn exec_query_plan(
178 &self,
179 plan: LogicalPlan,
180 query_ctx: QueryContextRef,
181 ) -> Result<Output> {
182 let mut ctx = self.engine_context(query_ctx.clone());
183 let plan = if let Some(receiver_injector) =
184 self.plugins.get::<RemoteDynFilterReceiverInjectorRef>()
185 {
186 receiver_injector.maybe_inject(plan, query_ctx.clone())
187 } else {
188 plan
189 };
190
191 let physical_plan = self.create_physical_plan(&mut ctx, &plan).await?;
193 let physical_plan = self.optimize_physical_plan(&mut ctx, physical_plan)?;
194 let physical_plan = if let Some(wrapper) = self.plugins.get::<PhysicalPlanWrapperRef>() {
195 wrapper.wrap(physical_plan, query_ctx)
196 } else {
197 physical_plan
198 };
199
200 let stream = self.execute_stream(&ctx, &physical_plan)?;
201
202 Ok(Output::new(
203 OutputData::Stream(stream),
204 OutputMeta::new_with_plan(physical_plan),
205 ))
206 }
207
208 #[tracing::instrument(skip_all)]
209 async fn exec_dml_statement(
210 &self,
211 dml: DmlStatement,
212 query_ctx: QueryContextRef,
213 ) -> Result<Output> {
214 ensure!(
215 matches!(dml.op, WriteOp::Insert(_) | WriteOp::Delete),
216 UnsupportedExprSnafu {
217 name: format!("DML op {}", dml.op),
218 }
219 );
220
221 let _timer = QUERY_STAGE_ELAPSED
222 .with_label_values(&[dml.op.name()])
223 .start_timer();
224
225 let default_catalog = &query_ctx.current_catalog().to_owned();
226 let default_schema = &query_ctx.current_schema();
227 let table_name = dml.table_name.resolve(default_catalog, default_schema);
228 let table = self.find_table(&table_name, &query_ctx).await?;
229
230 let Output { data, meta } = self
231 .exec_query_plan((*dml.input).clone(), query_ctx.clone())
232 .await?;
233 let stream = match data {
234 OutputData::RecordBatches(batches) => batches.as_stream(),
235 OutputData::Stream(stream) => stream,
236 _ => unreachable!(),
237 };
238
239 let mut affected_rows = 0;
240 let mut insert_cost = 0;
241
242 match dml.op {
243 WriteOp::Insert(_) => {
244 let (batch_tx, batch_rx) = tokio::sync::mpsc::unbounded_channel();
245 let producer = forward_record_batches(stream, batch_tx);
246 let consumer = self.consume_insert_record_batches(
247 batch_rx,
248 &table_name,
249 table.schema(),
250 query_ctx.clone(),
251 );
252 let ((), (rows, cost)) = try_join(producer, consumer).await?;
253 affected_rows += rows;
254 insert_cost += cost;
255 }
256 WriteOp::Delete => {
257 let (batch_tx, batch_rx) = tokio::sync::mpsc::unbounded_channel();
260 let producer = forward_record_batches(stream, batch_tx);
261 let consumer = self.consume_delete_record_batches(
262 batch_rx,
263 &table_name,
264 &table,
265 query_ctx.clone(),
266 );
267 let (rows, ()) = try_join(consumer, producer).await?;
268 affected_rows += rows;
269 }
270 _ => unreachable!("guarded by the 'ensure!' at the beginning"),
271 }
272 Ok(Output::new(
273 OutputData::AffectedRows(affected_rows),
274 OutputMeta::new(meta.plan, insert_cost),
275 ))
276 }
277
278 async fn consume_insert_record_batches(
279 &self,
280 mut batch_rx: tokio::sync::mpsc::UnboundedReceiver<Result<RecordBatch>>,
281 table_name: &ResolvedTableReference,
282 table_schema: Arc<Schema>,
283 query_ctx: QueryContextRef,
284 ) -> Result<(usize, usize)> {
285 let mut affected_rows = 0;
286 let mut insert_cost = 0;
287 while let Some(batch) = batch_rx.recv().await {
288 let batch = batch?;
289 let column_vectors = batch
290 .column_vectors(&table_name.to_string(), table_schema.clone())
291 .map_err(BoxedError::new)
292 .context(QueryExecutionSnafu)?;
293 let output = self
295 .insert(table_name, column_vectors, query_ctx.clone())
296 .await?;
297 let (rows, cost) = output.extract_rows_and_cost();
298 affected_rows += rows;
299 insert_cost += cost;
300 }
301 Ok((affected_rows, insert_cost))
302 }
303
304 async fn consume_delete_record_batches(
305 &self,
306 mut batch_rx: tokio::sync::mpsc::UnboundedReceiver<Result<RecordBatch>>,
307 table_name: &ResolvedTableReference,
308 table: &TableRef,
309 query_ctx: QueryContextRef,
310 ) -> Result<usize> {
311 let mut affected_rows = 0;
312 while let Some(batch) = batch_rx.recv().await {
313 let batch = batch?;
314 let column_vectors = batch
315 .column_vectors(&table_name.to_string(), table.schema())
316 .map_err(BoxedError::new)
317 .context(QueryExecutionSnafu)?;
318 affected_rows += self
319 .delete(table_name, table, column_vectors, query_ctx.clone())
320 .await?;
321 }
322 Ok(affected_rows)
323 }
324
325 #[tracing::instrument(skip_all)]
326 async fn delete(
327 &self,
328 table_name: &ResolvedTableReference,
329 table: &TableRef,
330 column_vectors: HashMap<String, VectorRef>,
331 query_ctx: QueryContextRef,
332 ) -> Result<usize> {
333 let catalog_name = table_name.catalog.to_string();
334 let schema_name = table_name.schema.to_string();
335 let table_name = table_name.table.to_string();
336 let table_schema = table.schema();
337
338 ensure!(
339 !is_readonly_table(&schema_name, &table_name),
340 TableReadOnlySnafu { table: table_name }
341 );
342
343 let ts_column = table_schema
344 .timestamp_column()
345 .map(|x| &x.name)
346 .with_context(|| MissingTimestampColumnSnafu {
347 table_name: table_name.clone(),
348 })?;
349
350 let table_info = table.table_info();
351 let rowkey_columns = table_info
352 .meta
353 .row_key_column_names()
354 .collect::<Vec<&String>>();
355 let column_vectors = column_vectors
356 .into_iter()
357 .filter(|x| &x.0 == ts_column || rowkey_columns.contains(&&x.0))
358 .collect::<HashMap<_, _>>();
359
360 let request = DeleteRequest {
361 catalog_name,
362 schema_name,
363 table_name,
364 key_column_values: column_vectors,
365 };
366
367 self.state
368 .table_mutation_handler()
369 .context(MissingTableMutationHandlerSnafu)?
370 .delete(request, query_ctx)
371 .await
372 .context(TableMutationSnafu)
373 }
374
375 #[tracing::instrument(skip_all)]
376 async fn insert(
377 &self,
378 table_name: &ResolvedTableReference,
379 column_vectors: HashMap<String, VectorRef>,
380 query_ctx: QueryContextRef,
381 ) -> Result<Output> {
382 let catalog_name = table_name.catalog.to_string();
383 let schema_name = table_name.schema.to_string();
384 let table_name = table_name.table.to_string();
385
386 ensure!(
387 !is_readonly_table(&schema_name, &table_name),
388 TableReadOnlySnafu { table: table_name }
389 );
390
391 let request = InsertRequest {
392 catalog_name,
393 schema_name,
394 table_name,
395 columns_values: column_vectors,
396 skip_wal: query_ctx.skip_wal(),
397 };
398
399 self.state
400 .table_mutation_handler()
401 .context(MissingTableMutationHandlerSnafu)?
402 .insert(request, query_ctx)
403 .await
404 .context(TableMutationSnafu)
405 }
406
407 async fn find_table(
408 &self,
409 table_name: &ResolvedTableReference,
410 query_context: &QueryContextRef,
411 ) -> Result<TableRef> {
412 let catalog_name = table_name.catalog.as_ref();
413 let schema_name = table_name.schema.as_ref();
414 let table_name = table_name.table.as_ref();
415
416 self.state
417 .catalog_manager()
418 .table(catalog_name, schema_name, table_name, Some(query_context))
419 .await
420 .context(CatalogSnafu)?
421 .with_context(|| TableNotFoundSnafu { table: table_name })
422 }
423
424 #[tracing::instrument(skip_all)]
425 async fn create_physical_plan(
426 &self,
427 ctx: &mut QueryEngineContext,
428 logical_plan: &LogicalPlan,
429 ) -> Result<Arc<dyn ExecutionPlan>> {
430 #[derive(Debug)]
434 struct PanicLogger<'a> {
435 input_logical_plan: &'a LogicalPlan,
436 after_analyze: Option<LogicalPlan>,
437 after_optimize: Option<LogicalPlan>,
438 phy_plan: Option<Arc<dyn ExecutionPlan>>,
439 }
440 impl Drop for PanicLogger<'_> {
441 fn drop(&mut self) {
442 if std::thread::panicking() {
443 common_telemetry::error!(
444 "Panic while creating physical plan, input logical plan: {:?}, after analyze: {:?}, after optimize: {:?}, final physical plan: {:?}",
445 self.input_logical_plan,
446 self.after_analyze,
447 self.after_optimize,
448 self.phy_plan
449 );
450 }
451 }
452 }
453
454 let mut logger = PanicLogger {
455 input_logical_plan: logical_plan,
456 after_analyze: None,
457 after_optimize: None,
458 phy_plan: None,
459 };
460
461 let _timer = metrics::CREATE_PHYSICAL_ELAPSED.start_timer();
462 let state = ctx.state();
463
464 common_telemetry::debug!("Create physical plan, input plan: {logical_plan}");
465
466 if matches!(logical_plan, DfLogicalPlan::Explain(_)) {
468 return state
469 .create_physical_plan(logical_plan)
470 .await
471 .map_err(Into::into);
472 }
473
474 let analyzed_plan = state.analyzer().execute_and_check(
476 logical_plan.clone(),
477 state.config_options(),
478 |_, _| {},
479 )?;
480
481 logger.after_analyze = Some(analyzed_plan.clone());
482
483 common_telemetry::debug!("Create physical plan, analyzed plan: {analyzed_plan}");
484
485 let optimized_plan = if let DfLogicalPlan::Extension(ext) = &analyzed_plan
487 && ext.node.name() == MergeScanLogicalPlan::name()
488 {
489 analyzed_plan.clone()
490 } else {
491 state
492 .optimizer()
493 .optimize(analyzed_plan, state, |_, _| {})?
494 };
495
496 common_telemetry::debug!("Create physical plan, optimized plan: {optimized_plan}");
497 logger.after_optimize = Some(optimized_plan.clone());
498
499 let physical_plan = state
500 .query_planner()
501 .create_physical_plan(&optimized_plan, state)
502 .await?;
503
504 logger.phy_plan = Some(physical_plan.clone());
505 drop(logger);
506 Ok(physical_plan)
507 }
508
509 #[tracing::instrument(skip_all)]
510 fn optimize_physical_plan(
511 &self,
512 ctx: &mut QueryEngineContext,
513 plan: Arc<dyn ExecutionPlan>,
514 ) -> Result<Arc<dyn ExecutionPlan>> {
515 let _timer = metrics::OPTIMIZE_PHYSICAL_ELAPSED.start_timer();
516
517 let optimized_plan = if let Some(analyze_plan) = plan.downcast_ref::<AnalyzeExec>() {
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 async_trait::async_trait;
818 use catalog::RegisterTableRequest;
819 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, NUMBERS_TABLE_ID};
820 use common_error::ext::BoxedError;
821 use common_recordbatch::{
822 EmptyRecordBatchStream, RecordBatch, SendableRecordBatchStream, util,
823 };
824 use datafusion::physical_plan::display::{DisplayAs, DisplayFormatType};
825 use datafusion::physical_plan::expressions::PhysicalSortExpr;
826 use datafusion::physical_plan::joins::{HashJoinExec, JoinOn, PartitionMode};
827 use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet;
828 use datafusion::physical_plan::{ExecutionPlan, PhysicalExpr};
829 use datafusion::prelude::{col, lit};
830 use datafusion_common::{JoinType, NullEquality, ScalarValue};
831 use datafusion_physical_expr::expressions::Column;
832 use datatypes::prelude::ConcreteDataType;
833 use datatypes::schema::{ColumnSchema, SchemaRef};
834 use datatypes::vectors::{Helper, UInt32Vector, VectorRef};
835 use session::context::{QueryContext, QueryContextBuilder};
836 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder, RegionMetadataRef};
837 use store_api::region_engine::{
838 PartitionRange, PrepareRequest, QueryScanContext, RegionScanner, ScannerProperties,
839 };
840 use store_api::storage::{RegionId, ScanRequest};
841 use table::metadata::{TableInfoBuilder, TableMetaBuilder};
842 use table::table::numbers::{NUMBERS_TABLE_NAME, NumbersTable};
843 use table::table::scan::RegionScanExec;
844
845 use super::*;
846 use crate::options::QueryOptions;
847 use crate::parser::{QueryLanguageParser, QueryStatement};
848 use crate::part_sort::PartSortExec;
849 use crate::query_engine::{QueryEngineFactory, QueryEngineRef};
850
851 #[derive(Debug)]
852 struct RecordingScanner {
853 schema: SchemaRef,
854 metadata: RegionMetadataRef,
855 properties: ScannerProperties,
856 update_calls: Arc<AtomicUsize>,
857 last_filter_len: Arc<AtomicUsize>,
858 }
859
860 impl RecordingScanner {
861 fn new(
862 schema: SchemaRef,
863 metadata: RegionMetadataRef,
864 update_calls: Arc<AtomicUsize>,
865 last_filter_len: Arc<AtomicUsize>,
866 ) -> Self {
867 Self {
868 schema,
869 metadata,
870 properties: ScannerProperties::default(),
871 update_calls,
872 last_filter_len,
873 }
874 }
875 }
876
877 impl RegionScanner for RecordingScanner {
878 fn name(&self) -> &str {
879 "RecordingScanner"
880 }
881
882 fn properties(&self) -> &ScannerProperties {
883 &self.properties
884 }
885
886 fn schema(&self) -> SchemaRef {
887 self.schema.clone()
888 }
889
890 fn metadata(&self) -> RegionMetadataRef {
891 self.metadata.clone()
892 }
893
894 fn prepare(&mut self, request: PrepareRequest) -> std::result::Result<(), BoxedError> {
895 self.properties.prepare(request);
896 Ok(())
897 }
898
899 fn scan_partition(
900 &self,
901 _ctx: &QueryScanContext,
902 _metrics_set: &ExecutionPlanMetricsSet,
903 _partition: usize,
904 ) -> std::result::Result<SendableRecordBatchStream, BoxedError> {
905 Ok(Box::pin(EmptyRecordBatchStream::new(self.schema.clone())))
906 }
907
908 fn has_predicate_without_region(&self) -> bool {
909 true
910 }
911
912 fn add_dyn_filter_to_predicate(
913 &mut self,
914 filter_exprs: Vec<Arc<dyn PhysicalExpr>>,
915 ) -> Vec<bool> {
916 self.update_calls.fetch_add(1, Ordering::Relaxed);
917 self.last_filter_len
918 .store(filter_exprs.len(), Ordering::Relaxed);
919 vec![true; filter_exprs.len()]
920 }
921
922 fn set_logical_region(&mut self, logical_region: bool) {
923 self.properties.set_logical_region(logical_region);
924 }
925
926 fn set_query_load_region_id(&mut self, region_id: store_api::storage::RegionId) {
927 self.properties.set_query_load_region_id(region_id);
928 }
929 }
930
931 impl DisplayAs for RecordingScanner {
932 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result {
933 write!(f, "RecordingScanner")
934 }
935 }
936
937 fn build_query_load_region_scan(
938 query_load_region_id: Option<RegionId>,
939 ) -> Arc<dyn ExecutionPlan> {
940 build_region_scan(query_load_region_id, None)
941 }
942
943 fn build_query_stat_counter_region_scan(
944 counters: RegionQueryStatCounters,
945 ) -> Arc<dyn ExecutionPlan> {
946 build_region_scan(None, Some(counters))
947 }
948
949 fn build_region_scan(
950 query_load_region_id: Option<RegionId>,
951 query_stat_counters: Option<RegionQueryStatCounters>,
952 ) -> Arc<dyn ExecutionPlan> {
953 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
954 "ts",
955 ConcreteDataType::timestamp_millisecond_datatype(),
956 false,
957 )]));
958
959 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
960 metadata_builder
961 .push_column_metadata(ColumnMetadata {
962 column_schema: ColumnSchema::new(
963 "ts",
964 ConcreteDataType::timestamp_millisecond_datatype(),
965 false,
966 )
967 .with_time_index(true),
968 semantic_type: SemanticType::Timestamp,
969 column_id: 1,
970 })
971 .primary_key(vec![]);
972 let metadata = Arc::new(metadata_builder.build().unwrap());
973 let mut scanner = RecordingScanner::new(
974 schema,
975 metadata,
976 Arc::new(AtomicUsize::new(0)),
977 Arc::new(AtomicUsize::new(0)),
978 );
979 if let Some(region_id) = query_load_region_id {
980 scanner.set_query_load_region_id(region_id);
981 }
982 if let Some(counters) = query_stat_counters {
983 scanner.properties.set_query_stat_counters(counters);
984 }
985
986 Arc::new(RegionScanExec::new(Box::new(scanner), ScanRequest::default(), None).unwrap())
987 }
988
989 fn query_stat_counters_for_test() -> RegionQueryStatCounters {
990 RegionQueryStatCounters {
991 query_cpu_time: Arc::new(AtomicU64::new(0)),
992 query_scanned_bytes: Arc::new(AtomicU64::new(0)),
993 }
994 }
995
996 #[test]
997 fn query_load_region_id_ignores_scans_without_region_id() {
998 let query_load_region_id = RegionId::new(1024, 42);
999 let scan_without_region_id = build_query_load_region_scan(None);
1000 let scan_with_region_id = build_query_load_region_scan(Some(query_load_region_id));
1001 let on: JoinOn = vec![(
1002 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1003 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1004 )];
1005 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1006 HashJoinExec::try_new(
1007 scan_without_region_id,
1008 scan_with_region_id,
1009 on,
1010 None,
1011 &JoinType::Inner,
1012 None,
1013 PartitionMode::CollectLeft,
1014 NullEquality::NullEqualsNull,
1015 false,
1016 )
1017 .unwrap(),
1018 );
1019
1020 assert_eq!(
1021 super::query_load_region_id(&plan),
1022 Some(query_load_region_id.as_u64())
1023 );
1024 }
1025
1026 #[test]
1027 fn query_stat_counters_returns_shared_counter_for_multi_scan_plan() {
1028 let counters = query_stat_counters_for_test();
1029 let left = build_query_stat_counter_region_scan(counters.clone());
1030 let right = build_query_stat_counter_region_scan(counters.clone());
1031 let on: JoinOn = vec![(
1032 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1033 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1034 )];
1035 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1036 HashJoinExec::try_new(
1037 left,
1038 right,
1039 on,
1040 None,
1041 &JoinType::Inner,
1042 None,
1043 PartitionMode::CollectLeft,
1044 NullEquality::NullEqualsNull,
1045 false,
1046 )
1047 .unwrap(),
1048 );
1049
1050 let actual = super::query_stat_counters(&plan).unwrap();
1051 assert!(Arc::ptr_eq(
1052 &actual.query_cpu_time,
1053 &counters.query_cpu_time
1054 ));
1055 assert!(Arc::ptr_eq(
1056 &actual.query_scanned_bytes,
1057 &counters.query_scanned_bytes
1058 ));
1059 }
1060
1061 #[test]
1062 fn query_stat_counters_ignores_mixed_counter_plan() {
1063 let left = build_query_stat_counter_region_scan(query_stat_counters_for_test());
1064 let right = build_query_stat_counter_region_scan(query_stat_counters_for_test());
1065 let on: JoinOn = vec![(
1066 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1067 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1068 )];
1069 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1070 HashJoinExec::try_new(
1071 left,
1072 right,
1073 on,
1074 None,
1075 &JoinType::Inner,
1076 None,
1077 PartitionMode::CollectLeft,
1078 NullEquality::NullEqualsNull,
1079 false,
1080 )
1081 .unwrap(),
1082 );
1083
1084 assert!(super::query_stat_counters(&plan).is_none());
1085 }
1086
1087 async fn create_test_engine() -> QueryEngineRef {
1088 let catalog_manager = catalog::memory::new_memory_catalog_manager().unwrap();
1089 let req = RegisterTableRequest {
1090 catalog: DEFAULT_CATALOG_NAME.to_string(),
1091 schema: DEFAULT_SCHEMA_NAME.to_string(),
1092 table_name: NUMBERS_TABLE_NAME.to_string(),
1093 table_id: NUMBERS_TABLE_ID,
1094 table: NumbersTable::table(NUMBERS_TABLE_ID),
1095 };
1096 catalog_manager.register_table_sync(req).unwrap();
1097
1098 QueryEngineFactory::new(
1099 catalog_manager,
1100 None,
1101 None,
1102 None,
1103 None,
1104 false,
1105 QueryOptions::default(),
1106 )
1107 .query_engine()
1108 }
1109
1110 #[tokio::test]
1111 async fn test_sql_to_plan() {
1112 let engine = create_test_engine().await;
1113 let sql = "select sum(number) from numbers limit 20";
1114
1115 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1116 let plan = engine
1117 .planner()
1118 .plan(&stmt, QueryContext::arc())
1119 .await
1120 .unwrap();
1121
1122 assert_eq!(
1123 plan.to_string(),
1124 r#"Limit: skip=0, fetch=20
1125 Projection: sum(numbers.number)
1126 Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]
1127 TableScan: numbers"#
1128 );
1129 }
1130
1131 #[tokio::test]
1132 async fn test_purge_table_is_not_available_to_select() {
1133 let engine = create_test_engine().await;
1134 let stmt =
1135 QueryLanguageParser::parse_sql("select purge_table('numbers')", &QueryContext::arc())
1136 .unwrap();
1137
1138 assert!(
1139 engine
1140 .planner()
1141 .plan(&stmt, QueryContext::arc())
1142 .await
1143 .is_err()
1144 );
1145 }
1146
1147 #[tokio::test]
1148 async fn test_execute() {
1149 let engine = create_test_engine().await;
1150 let sql = "select sum(number) from numbers limit 20";
1151
1152 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1153 let plan = engine
1154 .planner()
1155 .plan(&stmt, QueryContext::arc())
1156 .await
1157 .unwrap();
1158
1159 let output = engine.execute(plan, QueryContext::arc()).await.unwrap();
1160
1161 match output.data {
1162 OutputData::Stream(recordbatch) => {
1163 let numbers = util::collect(recordbatch).await.unwrap();
1164 assert_eq!(1, numbers.len());
1165 assert_eq!(numbers[0].num_columns(), 1);
1166 assert_eq!(1, numbers[0].schema.num_columns());
1167 assert_eq!(
1168 "sum(numbers.number)",
1169 numbers[0].schema.column_schemas()[0].name
1170 );
1171
1172 let batch = &numbers[0];
1173 assert_eq!(1, batch.num_columns());
1174 assert_eq!(batch.column(0).len(), 1);
1175
1176 let expected = Arc::new(UInt64Array::from_iter_values([4950])) as ArrayRef;
1177 assert_eq!(batch.column(0), &expected);
1178 }
1179 _ => unreachable!(),
1180 }
1181 }
1182
1183 #[tokio::test]
1184 async fn test_read_table() {
1185 let engine = create_test_engine().await;
1186
1187 let engine = engine
1188 .as_any()
1189 .downcast_ref::<DatafusionQueryEngine>()
1190 .unwrap();
1191 let query_ctx = Arc::new(QueryContextBuilder::default().build());
1192 let table = engine
1193 .find_table(
1194 &ResolvedTableReference {
1195 catalog: "greptime".into(),
1196 schema: "public".into(),
1197 table: "numbers".into(),
1198 },
1199 &query_ctx,
1200 )
1201 .await
1202 .unwrap();
1203
1204 let df = engine.read_table(table).unwrap();
1205 let df = df
1206 .select_columns(&["number"])
1207 .unwrap()
1208 .filter(col("number").lt(lit(10)))
1209 .unwrap();
1210 let batches = df.collect().await.unwrap();
1211 assert_eq!(1, batches.len());
1212 let batch = &batches[0];
1213
1214 assert_eq!(1, batch.num_columns());
1215 assert_eq!(batch.column(0).len(), 10);
1216
1217 assert_eq!(
1218 Helper::try_into_vector(batch.column(0)).unwrap(),
1219 Arc::new(UInt32Vector::from_slice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) as VectorRef
1220 );
1221 }
1222
1223 #[tokio::test]
1224 async fn test_describe() {
1225 let engine = create_test_engine().await;
1226 let sql = "select sum(number) from numbers limit 20";
1227
1228 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1229
1230 let plan = engine
1231 .planner()
1232 .plan(&stmt, QueryContext::arc())
1233 .await
1234 .unwrap();
1235
1236 let DescribeResult { logical_plan } =
1237 engine.describe(plan, QueryContext::arc()).await.unwrap();
1238
1239 let schema: Schema = logical_plan.schema().clone().try_into().unwrap();
1240
1241 assert_eq!(
1242 schema.column_schemas()[0],
1243 ColumnSchema::new(
1244 "sum(numbers.number)",
1245 ConcreteDataType::uint64_datatype(),
1246 true
1247 )
1248 );
1249 assert_eq!(
1250 "Limit: skip=0, fetch=20\n Projection: sum(numbers.number)\n Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]\n TableScan: numbers",
1251 format!("{}", logical_plan.display_indent())
1252 );
1253 }
1254
1255 #[tokio::test]
1256 async fn test_topk_dynamic_filter_pushdown_reaches_region_scan() {
1257 let engine = create_test_engine().await;
1258 let engine = engine
1259 .as_any()
1260 .downcast_ref::<DatafusionQueryEngine>()
1261 .unwrap();
1262 let engine_ctx = engine.engine_context(QueryContext::arc());
1263 let state = engine_ctx.state();
1264
1265 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1266 "ts",
1267 ConcreteDataType::timestamp_millisecond_datatype(),
1268 false,
1269 )]));
1270
1271 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
1272 metadata_builder
1273 .push_column_metadata(ColumnMetadata {
1274 column_schema: ColumnSchema::new(
1275 "ts",
1276 ConcreteDataType::timestamp_millisecond_datatype(),
1277 false,
1278 )
1279 .with_time_index(true),
1280 semantic_type: SemanticType::Timestamp,
1281 column_id: 1,
1282 })
1283 .primary_key(vec![]);
1284 let metadata = Arc::new(metadata_builder.build().unwrap());
1285
1286 let update_calls = Arc::new(AtomicUsize::new(0));
1287 let last_filter_len = Arc::new(AtomicUsize::new(0));
1288 let scanner = Box::new(RecordingScanner::new(
1289 schema,
1290 metadata,
1291 update_calls.clone(),
1292 last_filter_len.clone(),
1293 ));
1294 let scan = Arc::new(RegionScanExec::new(scanner, ScanRequest::default(), None).unwrap());
1295
1296 let sort_expr = PhysicalSortExpr {
1297 expr: Arc::new(Column::new("ts", 0)),
1298 options: SortOptions {
1299 descending: true,
1300 ..Default::default()
1301 },
1302 };
1303 let partition_ranges: Vec<Vec<PartitionRange>> = vec![vec![]];
1304 let mut plan: Arc<dyn ExecutionPlan> =
1305 Arc::new(PartSortExec::try_new(sort_expr, Some(3), partition_ranges, scan).unwrap());
1306
1307 for optimizer in state.physical_optimizers() {
1308 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1309 }
1310
1311 assert!(update_calls.load(Ordering::Relaxed) > 0);
1312 assert!(last_filter_len.load(Ordering::Relaxed) > 0);
1313 }
1314
1315 #[tokio::test]
1316 async fn test_join_dynamic_filter_pushdown_reaches_region_scan() {
1317 let engine = create_test_engine().await;
1318 let engine = engine
1319 .as_any()
1320 .downcast_ref::<DatafusionQueryEngine>()
1321 .unwrap();
1322 let engine_ctx = engine.engine_context(QueryContext::arc());
1323 let state = engine_ctx.state();
1324
1325 assert!(
1326 state
1327 .config_options()
1328 .optimizer
1329 .enable_join_dynamic_filter_pushdown
1330 );
1331
1332 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1333 "ts",
1334 ConcreteDataType::timestamp_millisecond_datatype(),
1335 false,
1336 )]));
1337
1338 let mut left_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 1));
1339 left_metadata_builder
1340 .push_column_metadata(ColumnMetadata {
1341 column_schema: ColumnSchema::new(
1342 "ts",
1343 ConcreteDataType::timestamp_millisecond_datatype(),
1344 false,
1345 )
1346 .with_time_index(true),
1347 semantic_type: SemanticType::Timestamp,
1348 column_id: 1,
1349 })
1350 .primary_key(vec![]);
1351 let left_metadata = Arc::new(left_metadata_builder.build().unwrap());
1352
1353 let mut right_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 2));
1354 right_metadata_builder
1355 .push_column_metadata(ColumnMetadata {
1356 column_schema: ColumnSchema::new(
1357 "ts",
1358 ConcreteDataType::timestamp_millisecond_datatype(),
1359 false,
1360 )
1361 .with_time_index(true),
1362 semantic_type: SemanticType::Timestamp,
1363 column_id: 1,
1364 })
1365 .primary_key(vec![]);
1366 let right_metadata = Arc::new(right_metadata_builder.build().unwrap());
1367
1368 let left_update_calls = Arc::new(AtomicUsize::new(0));
1369 let left_last_filter_len = Arc::new(AtomicUsize::new(0));
1370 let right_update_calls = Arc::new(AtomicUsize::new(0));
1371 let right_last_filter_len = Arc::new(AtomicUsize::new(0));
1372
1373 let left_scan = Arc::new(
1374 RegionScanExec::new(
1375 Box::new(RecordingScanner::new(
1376 schema.clone(),
1377 left_metadata,
1378 left_update_calls.clone(),
1379 left_last_filter_len.clone(),
1380 )),
1381 ScanRequest::default(),
1382 None,
1383 )
1384 .unwrap(),
1385 );
1386 let right_scan = Arc::new(
1387 RegionScanExec::new(
1388 Box::new(RecordingScanner::new(
1389 schema,
1390 right_metadata,
1391 right_update_calls.clone(),
1392 right_last_filter_len.clone(),
1393 )),
1394 ScanRequest::default(),
1395 None,
1396 )
1397 .unwrap(),
1398 );
1399
1400 let on: JoinOn = vec![(
1401 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1402 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1403 )];
1404
1405 let mut plan: Arc<dyn ExecutionPlan> = Arc::new(
1406 HashJoinExec::try_new(
1407 left_scan,
1408 right_scan,
1409 on,
1410 None,
1411 &JoinType::Inner,
1412 None,
1413 PartitionMode::CollectLeft,
1414 NullEquality::NullEqualsNull,
1415 false,
1416 )
1417 .unwrap(),
1418 );
1419
1420 for optimizer in state.physical_optimizers() {
1421 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1422 }
1423
1424 assert!(left_update_calls.load(Ordering::Relaxed) > 0);
1425 assert_eq!(0, left_last_filter_len.load(Ordering::Relaxed));
1426 assert!(right_update_calls.load(Ordering::Relaxed) > 0);
1427 assert!(right_last_filter_len.load(Ordering::Relaxed) > 0);
1428 }
1429 #[derive(Default)]
1430 struct RecordingMutationHandler {
1431 inserts: std::sync::Mutex<Vec<table::requests::InsertRequest>>,
1432 }
1433
1434 #[async_trait]
1435 impl common_function::handlers::TableMutationHandler for RecordingMutationHandler {
1436 async fn insert(
1437 &self,
1438 request: table::requests::InsertRequest,
1439 _ctx: session::context::QueryContextRef,
1440 ) -> common_query::error::Result<common_query::Output> {
1441 self.inserts.lock().unwrap().push(request);
1442 Ok(common_query::Output::new_with_affected_rows(1))
1443 }
1444
1445 async fn delete(
1446 &self,
1447 _request: table::requests::DeleteRequest,
1448 _ctx: session::context::QueryContextRef,
1449 ) -> common_query::error::Result<common_base::AffectedRows> {
1450 unimplemented!("unexpected delete")
1451 }
1452
1453 async fn flush(
1454 &self,
1455 _request: table::requests::FlushTableRequest,
1456 _ctx: session::context::QueryContextRef,
1457 ) -> common_query::error::Result<common_base::AffectedRows> {
1458 unimplemented!("unexpected flush")
1459 }
1460
1461 async fn compact(
1462 &self,
1463 _request: table::requests::CompactTableRequest,
1464 _ctx: session::context::QueryContextRef,
1465 ) -> common_query::error::Result<common_base::AffectedRows> {
1466 unimplemented!("unexpected compact")
1467 }
1468
1469 async fn build_index(
1470 &self,
1471 _request: table::requests::BuildIndexTableRequest,
1472 _ctx: session::context::QueryContextRef,
1473 ) -> common_query::error::Result<common_base::AffectedRows> {
1474 unimplemented!("unexpected build_index")
1475 }
1476
1477 async fn flush_region(
1478 &self,
1479 _region_id: store_api::storage::RegionId,
1480 _ctx: session::context::QueryContextRef,
1481 ) -> common_query::error::Result<common_base::AffectedRows> {
1482 unimplemented!("unexpected flush_region")
1483 }
1484
1485 async fn compact_region(
1486 &self,
1487 _region_id: store_api::storage::RegionId,
1488 _ctx: session::context::QueryContextRef,
1489 ) -> common_query::error::Result<common_base::AffectedRows> {
1490 unimplemented!("unexpected compact_region")
1491 }
1492
1493 async fn discard_unflushed_data(
1494 &self,
1495 _region_id: store_api::storage::RegionId,
1496 _ctx: session::context::QueryContextRef,
1497 ) -> common_query::error::Result<common_base::AffectedRows> {
1498 unimplemented!("unexpected discard_unflushed_data")
1499 }
1500
1501 async fn discard_unflushed_data_by_table(
1502 &self,
1503 _table_name: table::table_name::TableName,
1504 _ctx: session::context::QueryContextRef,
1505 ) -> common_query::error::Result<common_base::AffectedRows> {
1506 unimplemented!("unexpected discard_unflushed_data_by_table")
1507 }
1508 }
1509
1510 fn native_schema() -> Arc<Schema> {
1511 Arc::new(Schema::new(vec![
1512 ColumnSchema::new("dim", ConcreteDataType::date_datatype(), true),
1513 ColumnSchema::new("amount", ConcreteDataType::decimal128_datatype(30, 2), true),
1514 ColumnSchema::new(
1515 "elapsed",
1516 ConcreteDataType::duration_millisecond_datatype(),
1517 true,
1518 ),
1519 ColumnSchema::new(
1520 "ts",
1521 ConcreteDataType::timestamp_millisecond_datatype(),
1522 false,
1523 )
1524 .with_time_index(true),
1525 ColumnSchema::new(
1526 "updated_at",
1527 ConcreteDataType::timestamp_millisecond_datatype(),
1528 true,
1529 ),
1530 ColumnSchema::new("marker", ConcreteDataType::uint8_datatype(), true),
1531 ColumnSchema::new("payload", ConcreteDataType::binary_datatype(), true),
1532 ColumnSchema::new("epoch", ConcreteDataType::uint64_datatype(), true),
1533 ]))
1534 }
1535
1536 fn register_native_tables(catalog: &catalog::memory::MemoryCatalogManager) {
1537 let schema = native_schema();
1538 let meta = TableMetaBuilder::empty()
1539 .schema(schema.clone())
1540 .primary_key_indices(vec![])
1541 .value_indices((0..schema.num_columns()).collect())
1542 .next_column_id(8)
1543 .build()
1544 .unwrap();
1545 let info = TableInfoBuilder::default()
1546 .name("native_regression")
1547 .table_id(9001)
1548 .table_version(0)
1549 .meta(meta)
1550 .build()
1551 .unwrap();
1552 catalog
1553 .register_table_sync(RegisterTableRequest {
1554 catalog: DEFAULT_CATALOG_NAME.to_string(),
1555 schema: DEFAULT_SCHEMA_NAME.to_string(),
1556 table_name: "native_regression".to_string(),
1557 table_id: 9001,
1558 table: table::test_util::EmptyTable::from_table_info(&info),
1559 })
1560 .unwrap();
1561
1562 let schema = Arc::new(Schema::new(vec![
1563 ColumnSchema::new("dim", ConcreteDataType::date_datatype(), true),
1564 ColumnSchema::new("amount", ConcreteDataType::decimal128_datatype(20, 2), true),
1565 ColumnSchema::new(
1566 "elapsed",
1567 ConcreteDataType::duration_millisecond_datatype(),
1568 true,
1569 ),
1570 ColumnSchema::new(
1571 "ts",
1572 ConcreteDataType::timestamp_millisecond_datatype(),
1573 false,
1574 )
1575 .with_time_index(true),
1576 ]));
1577 let rows = RecordBatch::new(
1578 schema,
1579 vec![
1580 Arc::new(datatypes::vectors::DateVector::from_slice([0, 2])) as VectorRef,
1581 Arc::new(
1582 datatypes::vectors::Decimal128Vector::from_slice([10000, 20000])
1583 .with_precision_and_scale(20, 2)
1584 .unwrap(),
1585 ) as VectorRef,
1586 Arc::new(datatypes::vectors::DurationMillisecondVector::from_values(
1587 [10, 20],
1588 )) as VectorRef,
1589 Arc::new(datatypes::vectors::TimestampMillisecondVector::from_slice(
1590 [1, 2],
1591 )) as VectorRef,
1592 ],
1593 )
1594 .unwrap();
1595 catalog
1596 .register_table_sync(RegisterTableRequest {
1597 catalog: DEFAULT_CATALOG_NAME.to_string(),
1598 schema: DEFAULT_SCHEMA_NAME.to_string(),
1599 table_name: "native_aggregate".to_string(),
1600 table_id: 9002,
1601 table: table::test_util::MemTable::table("native_aggregate", rows),
1602 })
1603 .unwrap();
1604 }
1605
1606 async fn run_sql(engine: &QueryEngineRef, sql: &str) -> Vec<RecordBatch> {
1607 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1608 let plan = engine
1609 .planner()
1610 .plan(&stmt, QueryContext::arc())
1611 .await
1612 .unwrap();
1613 match engine
1614 .execute(plan, QueryContext::arc())
1615 .await
1616 .unwrap()
1617 .data
1618 {
1619 OutputData::Stream(stream) => util::collect(stream).await.unwrap(),
1620 _ => unreachable!(),
1621 }
1622 }
1623
1624 #[tokio::test]
1625 async fn test_native_executor_null_insert_and_aggregates() {
1626 let catalog = catalog::memory::new_memory_catalog_manager().unwrap();
1627 register_native_tables(&catalog);
1628 let handler = Arc::new(RecordingMutationHandler::default());
1629 let engine = QueryEngineFactory::new(
1630 catalog,
1631 None,
1632 Some(handler.clone()),
1633 None,
1634 None,
1635 false,
1636 QueryOptions::default(),
1637 )
1638 .query_engine();
1639
1640 let insert_sql = "INSERT INTO native_regression (dim, amount, elapsed, ts, updated_at, marker, payload, epoch) VALUES (NULL, NULL, NULL, CAST(-62135596799999 AS TIMESTAMP(3)), CAST(NULL AS TIMESTAMP(3)), CAST(1 AS UInt8), X'0102', CAST(1 AS UInt64))";
1643 let stmt = QueryLanguageParser::parse_sql(insert_sql, &QueryContext::arc()).unwrap();
1644 let QueryStatement::Sql(sql::statements::statement::Statement::Insert(insert)) = &stmt
1645 else {
1646 unreachable!()
1647 };
1648 assert!(!insert.can_extract_values());
1649 let plan = engine
1650 .planner()
1651 .plan(&stmt, QueryContext::arc())
1652 .await
1653 .unwrap();
1654 assert!(matches!(
1655 engine
1656 .execute(plan, QueryContext::arc())
1657 .await
1658 .unwrap()
1659 .data,
1660 OutputData::AffectedRows(1)
1661 ));
1662
1663 let request = handler.inserts.lock().unwrap().pop().unwrap();
1664 for (name, data_type) in [
1665 ("dim", ConcreteDataType::date_datatype()),
1666 ("amount", ConcreteDataType::decimal128_datatype(30, 2)),
1667 ("elapsed", ConcreteDataType::duration_millisecond_datatype()),
1668 (
1669 "updated_at",
1670 ConcreteDataType::timestamp_millisecond_datatype(),
1671 ),
1672 ] {
1673 let vector = request.columns_values.get(name).unwrap();
1674 assert_eq!(vector.len(), 1, "{name}");
1675 assert_eq!(vector.data_type(), data_type, "{name}");
1676 assert!(vector.is_null(0), "{name}");
1677 }
1678 assert!(!request.columns_values["ts"].is_null(0));
1679 for (name, data_type) in [
1680 ("marker", ConcreteDataType::uint8_datatype()),
1681 ("payload", ConcreteDataType::binary_datatype()),
1682 ("epoch", ConcreteDataType::uint64_datatype()),
1683 ] {
1684 assert_eq!(
1685 request.columns_values[name].data_type(),
1686 data_type,
1687 "{name}"
1688 );
1689 }
1690
1691 let batches = run_sql(
1692 &engine,
1693 "SELECT MIN(dim), MAX(dim), SUM(amount), SUM(elapsed) FROM native_aggregate",
1694 )
1695 .await;
1696 let batch = &batches[0];
1697 assert_eq!(batch.num_rows(), 1);
1698 for (index, (data_type, value)) in [
1699 (
1700 ConcreteDataType::date_datatype(),
1701 ScalarValue::Date32(Some(0)),
1702 ),
1703 (
1704 ConcreteDataType::date_datatype(),
1705 ScalarValue::Date32(Some(2)),
1706 ),
1707 (
1708 ConcreteDataType::decimal128_datatype(30, 2),
1709 ScalarValue::Decimal128(Some(30000), 30, 2),
1710 ),
1711 (
1712 ConcreteDataType::duration_millisecond_datatype(),
1713 ScalarValue::DurationMillisecond(Some(30)),
1714 ),
1715 ]
1716 .into_iter()
1717 .enumerate()
1718 {
1719 assert_eq!(batch.schema.column_schemas()[index].data_type, data_type);
1720 assert_eq!(
1721 ScalarValue::try_from_array(batch.column(index).as_ref(), 0).unwrap(),
1722 value
1723 );
1724 }
1725
1726 let batches = run_sql(
1727 &engine,
1728 "SELECT aggregate.amount_sum + aggregate.amount_sum AS amount_add, \
1729 aggregate.elapsed_sum + aggregate.elapsed_sum AS elapsed_add, \
1730 CASE WHEN aggregate.min_dim < CAST('1970-01-02' AS DATE) THEN true ELSE false END AS min_before, \
1731 CASE WHEN aggregate.max_dim > CAST('1970-01-01' AS DATE) THEN true ELSE false END AS max_after \
1732 FROM (SELECT MIN(dim) AS min_dim, MAX(dim) AS max_dim, SUM(amount) AS amount_sum, SUM(elapsed) AS elapsed_sum \
1733 FROM native_aggregate) AS aggregate",
1734 )
1735 .await;
1736 let batch = &batches[0];
1737 assert_eq!(batch.num_rows(), 1);
1738 for (index, (data_type, value)) in [
1739 (
1740 ConcreteDataType::decimal128_datatype(31, 2),
1741 ScalarValue::Decimal128(Some(60000), 31, 2),
1742 ),
1743 (
1744 ConcreteDataType::duration_millisecond_datatype(),
1745 ScalarValue::DurationMillisecond(Some(60)),
1746 ),
1747 (
1748 ConcreteDataType::boolean_datatype(),
1749 ScalarValue::Boolean(Some(true)),
1750 ),
1751 (
1752 ConcreteDataType::boolean_datatype(),
1753 ScalarValue::Boolean(Some(true)),
1754 ),
1755 ]
1756 .into_iter()
1757 .enumerate()
1758 {
1759 assert_eq!(batch.schema.column_schemas()[index].data_type, data_type);
1760 assert_eq!(
1761 ScalarValue::try_from_array(batch.column(index).as_ref(), 0).unwrap(),
1762 value
1763 );
1764 }
1765 }
1766}