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, 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 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::optimizer::expr_depth;
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
85fn query_load_region_id(plan: &Arc<dyn ExecutionPlan>) -> Option<u64> {
86 let mut region_id = None;
87 let mut stack = vec![plan.clone()];
88
89 while let Some(plan) = stack.pop() {
90 if plan.name() == REGION_SCAN_EXEC_NAME
91 && let Some(scan) = plan.downcast_ref::<RegionScanExec>()
92 && let Some(scan_region_id) = scan.query_load_region_id()
93 {
94 match region_id {
95 Some(region_id) if region_id != scan_region_id => return None,
96 Some(_) => {}
97 None => region_id = Some(scan_region_id),
98 }
99 }
100 stack.extend(plan.children().into_iter().cloned());
101 }
102
103 region_id
104}
105
106fn query_stat_counters(plan: &Arc<dyn ExecutionPlan>) -> Option<RegionQueryStatCounters> {
115 let mut counters: Option<RegionQueryStatCounters> = None;
116 let mut stack = vec![plan.clone()];
117
118 while let Some(plan) = stack.pop() {
119 if plan.name() == REGION_SCAN_EXEC_NAME
120 && let Some(scan) = plan.downcast_ref::<RegionScanExec>()
121 && let Some(scan_counters) = scan.query_stat_counters()
122 {
123 match &counters {
124 Some(counters)
125 if !Arc::ptr_eq(&counters.query_cpu_time, &scan_counters.query_cpu_time)
126 || !Arc::ptr_eq(
127 &counters.query_scanned_bytes,
128 &scan_counters.query_scanned_bytes,
129 ) =>
130 {
131 return None;
132 }
133 Some(_) => {}
134 None => counters = Some(scan_counters),
135 }
136 }
137 stack.extend(plan.children().into_iter().cloned());
138 }
139
140 counters
141}
142
143pub struct DatafusionQueryEngine {
144 state: Arc<QueryEngineState>,
145 plugins: Plugins,
146}
147
148impl DatafusionQueryEngine {
149 pub fn new(state: Arc<QueryEngineState>, plugins: Plugins) -> Self {
150 Self { state, plugins }
151 }
152
153 #[tracing::instrument(skip_all)]
154 async fn exec_query_plan(
155 &self,
156 plan: LogicalPlan,
157 query_ctx: QueryContextRef,
158 ) -> Result<Output> {
159 let mut ctx = self.engine_context(query_ctx.clone());
160 let plan = if let Some(receiver_injector) =
161 self.plugins.get::<RemoteDynFilterReceiverInjectorRef>()
162 {
163 receiver_injector.maybe_inject(plan, query_ctx.clone())
164 } else {
165 plan
166 };
167
168 let physical_plan = self.create_physical_plan(&mut ctx, &plan).await?;
170 let physical_plan = self.optimize_physical_plan(&mut ctx, physical_plan)?;
171 let physical_plan = if let Some(wrapper) = self.plugins.get::<PhysicalPlanWrapperRef>() {
172 wrapper.wrap(physical_plan, query_ctx)
173 } else {
174 physical_plan
175 };
176
177 let stream = self.execute_stream(&ctx, &physical_plan)?;
178
179 Ok(Output::new(
180 OutputData::Stream(stream),
181 OutputMeta::new_with_plan(physical_plan),
182 ))
183 }
184
185 #[tracing::instrument(skip_all)]
186 async fn exec_dml_statement(
187 &self,
188 dml: DmlStatement,
189 query_ctx: QueryContextRef,
190 ) -> Result<Output> {
191 ensure!(
192 matches!(dml.op, WriteOp::Insert(_) | WriteOp::Delete),
193 UnsupportedExprSnafu {
194 name: format!("DML op {}", dml.op),
195 }
196 );
197
198 let _timer = QUERY_STAGE_ELAPSED
199 .with_label_values(&[dml.op.name()])
200 .start_timer();
201
202 let default_catalog = &query_ctx.current_catalog().to_owned();
203 let default_schema = &query_ctx.current_schema();
204 let table_name = dml.table_name.resolve(default_catalog, default_schema);
205 let table = self.find_table(&table_name, &query_ctx).await?;
206
207 let Output { data, meta } = self
208 .exec_query_plan((*dml.input).clone(), query_ctx.clone())
209 .await?;
210 let mut stream = match data {
211 OutputData::RecordBatches(batches) => batches.as_stream(),
212 OutputData::Stream(stream) => stream,
213 _ => unreachable!(),
214 };
215
216 let mut affected_rows = 0;
217 let mut insert_cost = 0;
218
219 match dml.op {
220 WriteOp::Insert(_) => {
221 while let Some(batch) = stream.next().await {
222 let batch = batch.context(CreateRecordBatchSnafu)?;
223 let column_vectors = batch
224 .column_vectors(&table_name.to_string(), table.schema())
225 .map_err(BoxedError::new)
226 .context(QueryExecutionSnafu)?;
227 let output = self
229 .insert(&table_name, column_vectors, query_ctx.clone())
230 .await?;
231 let (rows, cost) = output.extract_rows_and_cost();
232 affected_rows += rows;
233 insert_cost += cost;
234 }
235 }
236 WriteOp::Delete => {
237 while let Some(batch) = stream.next().await {
238 let batch = batch.context(CreateRecordBatchSnafu)?;
239 let column_vectors = batch
240 .column_vectors(&table_name.to_string(), table.schema())
241 .map_err(BoxedError::new)
242 .context(QueryExecutionSnafu)?;
243 affected_rows += self
244 .delete(&table_name, &table, column_vectors, query_ctx.clone())
245 .await?;
246 }
247 }
248 _ => unreachable!("guarded by the 'ensure!' at the beginning"),
249 }
250 Ok(Output::new(
251 OutputData::AffectedRows(affected_rows),
252 OutputMeta::new(meta.plan, insert_cost),
253 ))
254 }
255
256 #[tracing::instrument(skip_all)]
257 async fn delete(
258 &self,
259 table_name: &ResolvedTableReference,
260 table: &TableRef,
261 column_vectors: HashMap<String, VectorRef>,
262 query_ctx: QueryContextRef,
263 ) -> Result<usize> {
264 let catalog_name = table_name.catalog.to_string();
265 let schema_name = table_name.schema.to_string();
266 let table_name = table_name.table.to_string();
267 let table_schema = table.schema();
268
269 ensure!(
270 !is_readonly_table(&schema_name, &table_name),
271 TableReadOnlySnafu { table: table_name }
272 );
273
274 let ts_column = table_schema
275 .timestamp_column()
276 .map(|x| &x.name)
277 .with_context(|| MissingTimestampColumnSnafu {
278 table_name: table_name.clone(),
279 })?;
280
281 let table_info = table.table_info();
282 let rowkey_columns = table_info
283 .meta
284 .row_key_column_names()
285 .collect::<Vec<&String>>();
286 let column_vectors = column_vectors
287 .into_iter()
288 .filter(|x| &x.0 == ts_column || rowkey_columns.contains(&&x.0))
289 .collect::<HashMap<_, _>>();
290
291 let request = DeleteRequest {
292 catalog_name,
293 schema_name,
294 table_name,
295 key_column_values: column_vectors,
296 };
297
298 self.state
299 .table_mutation_handler()
300 .context(MissingTableMutationHandlerSnafu)?
301 .delete(request, query_ctx)
302 .await
303 .context(TableMutationSnafu)
304 }
305
306 #[tracing::instrument(skip_all)]
307 async fn insert(
308 &self,
309 table_name: &ResolvedTableReference,
310 column_vectors: HashMap<String, VectorRef>,
311 query_ctx: QueryContextRef,
312 ) -> Result<Output> {
313 let catalog_name = table_name.catalog.to_string();
314 let schema_name = table_name.schema.to_string();
315 let table_name = table_name.table.to_string();
316
317 ensure!(
318 !is_readonly_table(&schema_name, &table_name),
319 TableReadOnlySnafu { table: table_name }
320 );
321
322 let request = InsertRequest {
323 catalog_name,
324 schema_name,
325 table_name,
326 columns_values: column_vectors,
327 skip_wal: query_ctx.skip_wal(),
328 };
329
330 self.state
331 .table_mutation_handler()
332 .context(MissingTableMutationHandlerSnafu)?
333 .insert(request, query_ctx)
334 .await
335 .context(TableMutationSnafu)
336 }
337
338 async fn find_table(
339 &self,
340 table_name: &ResolvedTableReference,
341 query_context: &QueryContextRef,
342 ) -> Result<TableRef> {
343 let catalog_name = table_name.catalog.as_ref();
344 let schema_name = table_name.schema.as_ref();
345 let table_name = table_name.table.as_ref();
346
347 self.state
348 .catalog_manager()
349 .table(catalog_name, schema_name, table_name, Some(query_context))
350 .await
351 .context(CatalogSnafu)?
352 .with_context(|| TableNotFoundSnafu { table: table_name })
353 }
354
355 #[tracing::instrument(skip_all)]
356 async fn create_physical_plan(
357 &self,
358 ctx: &mut QueryEngineContext,
359 logical_plan: &LogicalPlan,
360 ) -> Result<Arc<dyn ExecutionPlan>> {
361 #[derive(Debug)]
365 struct PanicLogger<'a> {
366 input_logical_plan: &'a LogicalPlan,
367 after_analyze: Option<LogicalPlan>,
368 after_optimize: Option<LogicalPlan>,
369 phy_plan: Option<Arc<dyn ExecutionPlan>>,
370 }
371 impl Drop for PanicLogger<'_> {
372 fn drop(&mut self) {
373 if std::thread::panicking() {
374 common_telemetry::error!(
375 "Panic while creating physical plan, input logical plan: {:?}, after analyze: {:?}, after optimize: {:?}, final physical plan: {:?}",
376 self.input_logical_plan,
377 self.after_analyze,
378 self.after_optimize,
379 self.phy_plan
380 );
381 }
382 }
383 }
384
385 expr_depth::validate_plan(logical_plan)?;
386
387 let mut logger = PanicLogger {
388 input_logical_plan: logical_plan,
389 after_analyze: None,
390 after_optimize: None,
391 phy_plan: None,
392 };
393
394 let _timer = metrics::CREATE_PHYSICAL_ELAPSED.start_timer();
395 let state = ctx.state();
396
397 common_telemetry::debug!("Create physical plan, input plan: {logical_plan}");
398
399 if matches!(logical_plan, DfLogicalPlan::Explain(_)) {
401 return state
402 .create_physical_plan(logical_plan)
403 .await
404 .map_err(Into::into);
405 }
406
407 let analyzed_plan = state.analyzer().execute_and_check(
409 logical_plan.clone(),
410 state.config_options(),
411 |_, _| {},
412 )?;
413
414 expr_depth::validate_plan(&analyzed_plan)?;
415 logger.after_analyze = Some(analyzed_plan.clone());
416
417 common_telemetry::debug!("Create physical plan, analyzed plan: {analyzed_plan}");
418
419 let optimized_plan = if let DfLogicalPlan::Extension(ext) = &analyzed_plan
421 && ext.node.name() == MergeScanLogicalPlan::name()
422 {
423 analyzed_plan.clone()
424 } else {
425 state
426 .optimizer()
427 .optimize(analyzed_plan, state, |_, _| {})?
428 };
429
430 expr_depth::validate_plan(&optimized_plan)?;
431 common_telemetry::debug!("Create physical plan, optimized plan: {optimized_plan}");
432 logger.after_optimize = Some(optimized_plan.clone());
433
434 let physical_plan = state
435 .query_planner()
436 .create_physical_plan(&optimized_plan, state)
437 .await?;
438
439 logger.phy_plan = Some(physical_plan.clone());
440 drop(logger);
441 Ok(physical_plan)
442 }
443
444 #[tracing::instrument(skip_all)]
445 fn optimize_physical_plan(
446 &self,
447 ctx: &mut QueryEngineContext,
448 plan: Arc<dyn ExecutionPlan>,
449 ) -> Result<Arc<dyn ExecutionPlan>> {
450 let _timer = metrics::OPTIMIZE_PHYSICAL_ELAPSED.start_timer();
451
452 let optimized_plan = if let Some(analyze_plan) = plan.downcast_ref::<AnalyzeExec>() {
459 let format = if let Some(format) = ctx.query_ctx().explain_format()
460 && format.to_lowercase() == "json"
461 {
462 AnalyzeFormat::JSON
463 } else {
464 AnalyzeFormat::TEXT
465 };
466 ctx.query_ctx().set_explain_verbose(analyze_plan.verbose());
469
470 Arc::new(DistAnalyzeExec::new(
471 analyze_plan.input().clone(),
472 analyze_plan.verbose(),
473 format,
474 ))
475 } else {
483 plan
484 };
492
493 Ok(optimized_plan)
494 }
495}
496
497#[async_trait]
498impl QueryEngine for DatafusionQueryEngine {
499 fn as_any(&self) -> &dyn Any {
500 self
501 }
502
503 fn planner(&self) -> Arc<dyn LogicalPlanner> {
504 Arc::new(DfLogicalPlanner::new(self.state.clone()))
505 }
506
507 fn name(&self) -> &str {
508 "datafusion"
509 }
510
511 async fn describe(
512 &self,
513 plan: LogicalPlan,
514 _query_ctx: QueryContextRef,
515 ) -> Result<DescribeResult> {
516 Ok(DescribeResult { logical_plan: plan })
517 }
518
519 async fn execute(&self, plan: LogicalPlan, query_ctx: QueryContextRef) -> Result<Output> {
520 match plan {
521 LogicalPlan::Dml(dml) => self.exec_dml_statement(dml, query_ctx).await,
522 _ => self.exec_query_plan(plan, query_ctx).await,
523 }
524 }
525
526 fn register_aggregate_function(&self, func: AggregateUDF) {
534 self.state.register_aggr_function(func);
535 }
536
537 fn register_scalar_function(&self, func: ScalarFunctionFactory) {
540 self.state.register_scalar_function(func);
541 }
542
543 fn register_table_function(&self, func: Arc<TableFunction>) {
544 self.state.register_table_function(func);
545 }
546
547 fn register_window_function(&self, func: WindowUDF) {
548 self.state.register_window_function(func);
549 }
550
551 fn read_table(&self, table: TableRef) -> Result<DataFrame> {
552 self.state.read_table(table).map_err(Into::into)
553 }
554
555 fn engine_context(&self, query_ctx: QueryContextRef) -> QueryEngineContext {
556 let mut state = self.state.session_state();
557 state.config_mut().set_extension(query_ctx.clone());
558 state.config_mut().set_extension(self.state.clone());
559 if let Some(parallelism) = query_ctx.extension(QUERY_PARALLELISM_HINT) {
562 if let Ok(n) = parallelism.parse::<u64>() {
563 if n > 0 {
564 let new_cfg = state.config().clone().with_target_partitions(n as usize);
565 *state.config_mut() = new_cfg;
566 }
567 } else {
568 common_telemetry::warn!(
569 "Failed to parse query_parallelism: {}, using default value",
570 parallelism
571 );
572 }
573 }
574
575 state.config_mut().options_mut().execution.time_zone =
577 Some(query_ctx.timezone().to_string());
578
579 if query_ctx.configuration_parameter().allow_query_fallback() {
582 state
583 .config_mut()
584 .options_mut()
585 .extensions
586 .insert(DistPlannerOptions {
587 allow_query_fallback: true,
588 });
589 } else if let Some(fallback) = query_ctx.extension(QUERY_FALLBACK_HINT) {
590 if fallback.to_lowercase().parse::<bool>().unwrap_or(false) {
593 state
594 .config_mut()
595 .options_mut()
596 .extensions
597 .insert(DistPlannerOptions {
598 allow_query_fallback: true,
599 });
600 }
601 }
602
603 state
604 .config_mut()
605 .options_mut()
606 .extensions
607 .insert(FunctionContext {
608 query_ctx: query_ctx.clone(),
609 state: self.engine_state().function_state(),
610 });
611
612 state
616 .config_mut()
617 .options_mut()
618 .extensions
619 .insert(ScheduledTimeExtension {
620 scheduled_time: crate::options::scheduled_time_from_ctx(&query_ctx),
621 });
622
623 let config_options = state.config_options().clone();
624 let _ = state
625 .execution_props_mut()
626 .config_options
627 .insert(config_options);
628
629 match crate::options::parse_scheduled_time_datetime(&query_ctx.extensions()) {
633 Ok(Some(scheduled_rt)) => {
634 state.execution_props_mut().query_execution_start_time = Some(scheduled_rt);
635 }
636 Ok(None) => {}
637 Err(err) => {
638 common_telemetry::warn!(err; "Ignoring invalid scheduled time query extension");
639 }
640 }
641
642 QueryEngineContext::new(state, query_ctx)
643 }
644
645 fn engine_state(&self) -> &QueryEngineState {
646 &self.state
647 }
648}
649
650impl QueryExecutor for DatafusionQueryEngine {
651 #[tracing::instrument(skip_all)]
652 fn execute_stream(
653 &self,
654 ctx: &QueryEngineContext,
655 plan: &Arc<dyn ExecutionPlan>,
656 ) -> Result<SendableRecordBatchStream> {
657 let query_ctx = ctx.query_ctx();
658 let explain_verbose = query_ctx.explain_verbose();
659 let should_collect_region_watermark =
660 should_collect_region_watermark_from_query_ctx(&query_ctx)?;
661 let output_partitions = plan.properties().output_partitioning().partition_count();
662 if explain_verbose {
663 common_telemetry::info!("Executing query plan, output_partitions: {output_partitions}");
664 }
665
666 let exec_timer = metrics::EXEC_PLAN_ELAPSED.start_timer();
667 let task_ctx = ctx.build_task_ctx();
668 let span = Span::current();
669
670 match plan.properties().output_partitioning().partition_count() {
671 0 => {
672 let schema = Arc::new(
673 Schema::try_from(plan.schema())
674 .map_err(BoxedError::new)
675 .context(QueryExecutionSnafu)?,
676 );
677 Ok(Box::pin(EmptyRecordBatchStream::new(schema)))
678 }
679 1 => {
680 let df_stream = plan.execute(0, task_ctx)?;
681 let mut stream = RecordBatchStreamAdapter::try_new_with_span(df_stream, span)
682 .context(error::ConvertDfRecordBatchStreamSnafu)
683 .map_err(BoxedError::new)
684 .context(QueryExecutionSnafu)?;
685 stream.set_metrics2(plan.clone());
686 stream.set_query_load_region_id(query_load_region_id(plan));
687 stream.set_query_stat_counters(query_stat_counters(plan));
688 stream.set_explain_verbose(explain_verbose);
689 let stream = OnDone::new(Box::pin(stream), move || {
690 let exec_cost = exec_timer.stop_and_record();
691 if explain_verbose {
692 common_telemetry::info!(
693 "DatafusionQueryEngine execute 1 stream, cost: {:?}s",
694 exec_cost,
695 );
696 }
697 });
698 Ok(maybe_attach_region_watermark_metrics(
699 Box::pin(stream),
700 plan.clone(),
701 should_collect_region_watermark,
702 ))
703 }
704 _ => {
705 let merged_plan = CoalescePartitionsExec::new(plan.clone());
707 assert_eq!(
709 1,
710 merged_plan
711 .properties()
712 .output_partitioning()
713 .partition_count()
714 );
715 let df_stream = merged_plan.execute(0, task_ctx)?;
716 let mut stream = RecordBatchStreamAdapter::try_new_with_span(df_stream, span)
717 .context(error::ConvertDfRecordBatchStreamSnafu)
718 .map_err(BoxedError::new)
719 .context(QueryExecutionSnafu)?;
720 stream.set_metrics2(plan.clone());
721 stream.set_query_load_region_id(query_load_region_id(plan));
722 stream.set_query_stat_counters(query_stat_counters(plan));
723 stream.set_explain_verbose(explain_verbose);
724 let stream = OnDone::new(Box::pin(stream), move || {
725 let exec_cost = exec_timer.stop_and_record();
726 if explain_verbose {
727 common_telemetry::info!(
728 "DatafusionQueryEngine execute {output_partitions} stream, cost: {:?}s",
729 exec_cost
730 );
731 }
732 });
733 Ok(maybe_attach_region_watermark_metrics(
734 Box::pin(stream),
735 plan.clone(),
736 should_collect_region_watermark,
737 ))
738 }
739 }
740 }
741}
742
743#[cfg(test)]
744mod tests {
745 use std::fmt;
746 use std::sync::Arc;
747 use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
748
749 use api::v1::SemanticType;
750 use arrow::array::{ArrayRef, UInt64Array};
751 use arrow_schema::SortOptions;
752 use async_trait::async_trait;
753 use catalog::RegisterTableRequest;
754 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, NUMBERS_TABLE_ID};
755 use common_error::ext::BoxedError;
756 use common_recordbatch::{
757 EmptyRecordBatchStream, RecordBatch, SendableRecordBatchStream, util,
758 };
759 use datafusion::physical_plan::display::{DisplayAs, DisplayFormatType};
760 use datafusion::physical_plan::expressions::PhysicalSortExpr;
761 use datafusion::physical_plan::joins::{HashJoinExec, JoinOn, PartitionMode};
762 use datafusion::physical_plan::metrics::ExecutionPlanMetricsSet;
763 use datafusion::physical_plan::{ExecutionPlan, PhysicalExpr};
764 use datafusion::prelude::{col, lit};
765 use datafusion_common::{JoinType, NullEquality, ScalarValue};
766 use datafusion_physical_expr::expressions::Column;
767 use datatypes::prelude::ConcreteDataType;
768 use datatypes::schema::{ColumnSchema, SchemaRef};
769 use datatypes::vectors::{Helper, UInt32Vector, VectorRef};
770 use session::context::{QueryContext, QueryContextBuilder};
771 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder, RegionMetadataRef};
772 use store_api::region_engine::{
773 PartitionRange, PrepareRequest, QueryScanContext, RegionScanner, ScannerProperties,
774 };
775 use store_api::storage::{RegionId, ScanRequest};
776 use table::metadata::{TableInfoBuilder, TableMetaBuilder};
777 use table::table::numbers::{NUMBERS_TABLE_NAME, NumbersTable};
778 use table::table::scan::RegionScanExec;
779
780 use super::*;
781 use crate::options::QueryOptions;
782 use crate::parser::{QueryLanguageParser, QueryStatement};
783 use crate::part_sort::PartSortExec;
784 use crate::query_engine::{QueryEngineFactory, QueryEngineRef};
785
786 #[derive(Debug)]
787 struct RecordingScanner {
788 schema: SchemaRef,
789 metadata: RegionMetadataRef,
790 properties: ScannerProperties,
791 update_calls: Arc<AtomicUsize>,
792 last_filter_len: Arc<AtomicUsize>,
793 }
794
795 impl RecordingScanner {
796 fn new(
797 schema: SchemaRef,
798 metadata: RegionMetadataRef,
799 update_calls: Arc<AtomicUsize>,
800 last_filter_len: Arc<AtomicUsize>,
801 ) -> Self {
802 Self {
803 schema,
804 metadata,
805 properties: ScannerProperties::default(),
806 update_calls,
807 last_filter_len,
808 }
809 }
810 }
811
812 impl RegionScanner for RecordingScanner {
813 fn name(&self) -> &str {
814 "RecordingScanner"
815 }
816
817 fn properties(&self) -> &ScannerProperties {
818 &self.properties
819 }
820
821 fn schema(&self) -> SchemaRef {
822 self.schema.clone()
823 }
824
825 fn metadata(&self) -> RegionMetadataRef {
826 self.metadata.clone()
827 }
828
829 fn prepare(&mut self, request: PrepareRequest) -> std::result::Result<(), BoxedError> {
830 self.properties.prepare(request);
831 Ok(())
832 }
833
834 fn scan_partition(
835 &self,
836 _ctx: &QueryScanContext,
837 _metrics_set: &ExecutionPlanMetricsSet,
838 _partition: usize,
839 ) -> std::result::Result<SendableRecordBatchStream, BoxedError> {
840 Ok(Box::pin(EmptyRecordBatchStream::new(self.schema.clone())))
841 }
842
843 fn has_predicate_without_region(&self) -> bool {
844 true
845 }
846
847 fn add_dyn_filter_to_predicate(
848 &mut self,
849 filter_exprs: Vec<Arc<dyn PhysicalExpr>>,
850 ) -> Vec<bool> {
851 self.update_calls.fetch_add(1, Ordering::Relaxed);
852 self.last_filter_len
853 .store(filter_exprs.len(), Ordering::Relaxed);
854 vec![true; filter_exprs.len()]
855 }
856
857 fn set_logical_region(&mut self, logical_region: bool) {
858 self.properties.set_logical_region(logical_region);
859 }
860
861 fn set_query_load_region_id(&mut self, region_id: store_api::storage::RegionId) {
862 self.properties.set_query_load_region_id(region_id);
863 }
864 }
865
866 impl DisplayAs for RecordingScanner {
867 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868 write!(f, "RecordingScanner")
869 }
870 }
871
872 fn build_query_load_region_scan(
873 query_load_region_id: Option<RegionId>,
874 ) -> Arc<dyn ExecutionPlan> {
875 build_region_scan(query_load_region_id, None)
876 }
877
878 fn build_query_stat_counter_region_scan(
879 counters: RegionQueryStatCounters,
880 ) -> Arc<dyn ExecutionPlan> {
881 build_region_scan(None, Some(counters))
882 }
883
884 fn build_region_scan(
885 query_load_region_id: Option<RegionId>,
886 query_stat_counters: Option<RegionQueryStatCounters>,
887 ) -> Arc<dyn ExecutionPlan> {
888 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
889 "ts",
890 ConcreteDataType::timestamp_millisecond_datatype(),
891 false,
892 )]));
893
894 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
895 metadata_builder
896 .push_column_metadata(ColumnMetadata {
897 column_schema: ColumnSchema::new(
898 "ts",
899 ConcreteDataType::timestamp_millisecond_datatype(),
900 false,
901 )
902 .with_time_index(true),
903 semantic_type: SemanticType::Timestamp,
904 column_id: 1,
905 })
906 .primary_key(vec![]);
907 let metadata = Arc::new(metadata_builder.build().unwrap());
908 let mut scanner = RecordingScanner::new(
909 schema,
910 metadata,
911 Arc::new(AtomicUsize::new(0)),
912 Arc::new(AtomicUsize::new(0)),
913 );
914 if let Some(region_id) = query_load_region_id {
915 scanner.set_query_load_region_id(region_id);
916 }
917 if let Some(counters) = query_stat_counters {
918 scanner.properties.set_query_stat_counters(counters);
919 }
920
921 Arc::new(RegionScanExec::new(Box::new(scanner), ScanRequest::default(), None).unwrap())
922 }
923
924 fn query_stat_counters_for_test() -> RegionQueryStatCounters {
925 RegionQueryStatCounters {
926 query_cpu_time: Arc::new(AtomicU64::new(0)),
927 query_scanned_bytes: Arc::new(AtomicU64::new(0)),
928 }
929 }
930
931 #[test]
932 fn query_load_region_id_ignores_scans_without_region_id() {
933 let query_load_region_id = RegionId::new(1024, 42);
934 let scan_without_region_id = build_query_load_region_scan(None);
935 let scan_with_region_id = build_query_load_region_scan(Some(query_load_region_id));
936 let on: JoinOn = vec![(
937 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
938 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
939 )];
940 let plan: Arc<dyn ExecutionPlan> = Arc::new(
941 HashJoinExec::try_new(
942 scan_without_region_id,
943 scan_with_region_id,
944 on,
945 None,
946 &JoinType::Inner,
947 None,
948 PartitionMode::CollectLeft,
949 NullEquality::NullEqualsNull,
950 false,
951 )
952 .unwrap(),
953 );
954
955 assert_eq!(
956 super::query_load_region_id(&plan),
957 Some(query_load_region_id.as_u64())
958 );
959 }
960
961 #[test]
962 fn query_stat_counters_returns_shared_counter_for_multi_scan_plan() {
963 let counters = query_stat_counters_for_test();
964 let left = build_query_stat_counter_region_scan(counters.clone());
965 let right = build_query_stat_counter_region_scan(counters.clone());
966 let on: JoinOn = vec![(
967 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
968 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
969 )];
970 let plan: Arc<dyn ExecutionPlan> = Arc::new(
971 HashJoinExec::try_new(
972 left,
973 right,
974 on,
975 None,
976 &JoinType::Inner,
977 None,
978 PartitionMode::CollectLeft,
979 NullEquality::NullEqualsNull,
980 false,
981 )
982 .unwrap(),
983 );
984
985 let actual = super::query_stat_counters(&plan).unwrap();
986 assert!(Arc::ptr_eq(
987 &actual.query_cpu_time,
988 &counters.query_cpu_time
989 ));
990 assert!(Arc::ptr_eq(
991 &actual.query_scanned_bytes,
992 &counters.query_scanned_bytes
993 ));
994 }
995
996 #[test]
997 fn query_stat_counters_ignores_mixed_counter_plan() {
998 let left = build_query_stat_counter_region_scan(query_stat_counters_for_test());
999 let right = build_query_stat_counter_region_scan(query_stat_counters_for_test());
1000 let on: JoinOn = vec![(
1001 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1002 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1003 )];
1004 let plan: Arc<dyn ExecutionPlan> = Arc::new(
1005 HashJoinExec::try_new(
1006 left,
1007 right,
1008 on,
1009 None,
1010 &JoinType::Inner,
1011 None,
1012 PartitionMode::CollectLeft,
1013 NullEquality::NullEqualsNull,
1014 false,
1015 )
1016 .unwrap(),
1017 );
1018
1019 assert!(super::query_stat_counters(&plan).is_none());
1020 }
1021
1022 async fn create_test_engine() -> QueryEngineRef {
1023 let catalog_manager = catalog::memory::new_memory_catalog_manager().unwrap();
1024 let req = RegisterTableRequest {
1025 catalog: DEFAULT_CATALOG_NAME.to_string(),
1026 schema: DEFAULT_SCHEMA_NAME.to_string(),
1027 table_name: NUMBERS_TABLE_NAME.to_string(),
1028 table_id: NUMBERS_TABLE_ID,
1029 table: NumbersTable::table(NUMBERS_TABLE_ID),
1030 };
1031 catalog_manager.register_table_sync(req).unwrap();
1032
1033 QueryEngineFactory::new(
1034 catalog_manager,
1035 None,
1036 None,
1037 None,
1038 None,
1039 false,
1040 QueryOptions::default(),
1041 )
1042 .query_engine()
1043 }
1044
1045 #[tokio::test]
1046 async fn test_sql_to_plan() {
1047 let engine = create_test_engine().await;
1048 let sql = "select sum(number) from numbers limit 20";
1049
1050 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1051 let plan = engine
1052 .planner()
1053 .plan(&stmt, QueryContext::arc())
1054 .await
1055 .unwrap();
1056
1057 assert_eq!(
1058 plan.to_string(),
1059 r#"Limit: skip=0, fetch=20
1060 Projection: sum(numbers.number)
1061 Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]
1062 TableScan: numbers"#
1063 );
1064 }
1065
1066 #[tokio::test]
1067 async fn test_purge_table_is_not_available_to_select() {
1068 let engine = create_test_engine().await;
1069 let stmt =
1070 QueryLanguageParser::parse_sql("select purge_table('numbers')", &QueryContext::arc())
1071 .unwrap();
1072
1073 assert!(
1074 engine
1075 .planner()
1076 .plan(&stmt, QueryContext::arc())
1077 .await
1078 .is_err()
1079 );
1080 }
1081
1082 #[tokio::test]
1083 async fn test_execute() {
1084 let engine = create_test_engine().await;
1085 let sql = "select sum(number) from numbers limit 20";
1086
1087 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1088 let plan = engine
1089 .planner()
1090 .plan(&stmt, QueryContext::arc())
1091 .await
1092 .unwrap();
1093
1094 let output = engine.execute(plan, QueryContext::arc()).await.unwrap();
1095
1096 match output.data {
1097 OutputData::Stream(recordbatch) => {
1098 let numbers = util::collect(recordbatch).await.unwrap();
1099 assert_eq!(1, numbers.len());
1100 assert_eq!(numbers[0].num_columns(), 1);
1101 assert_eq!(1, numbers[0].schema.num_columns());
1102 assert_eq!(
1103 "sum(numbers.number)",
1104 numbers[0].schema.column_schemas()[0].name
1105 );
1106
1107 let batch = &numbers[0];
1108 assert_eq!(1, batch.num_columns());
1109 assert_eq!(batch.column(0).len(), 1);
1110
1111 let expected = Arc::new(UInt64Array::from_iter_values([4950])) as ArrayRef;
1112 assert_eq!(batch.column(0), &expected);
1113 }
1114 _ => unreachable!(),
1115 }
1116 }
1117
1118 #[tokio::test]
1119 async fn test_read_table() {
1120 let engine = create_test_engine().await;
1121
1122 let engine = engine
1123 .as_any()
1124 .downcast_ref::<DatafusionQueryEngine>()
1125 .unwrap();
1126 let query_ctx = Arc::new(QueryContextBuilder::default().build());
1127 let table = engine
1128 .find_table(
1129 &ResolvedTableReference {
1130 catalog: "greptime".into(),
1131 schema: "public".into(),
1132 table: "numbers".into(),
1133 },
1134 &query_ctx,
1135 )
1136 .await
1137 .unwrap();
1138
1139 let df = engine.read_table(table).unwrap();
1140 let df = df
1141 .select_columns(&["number"])
1142 .unwrap()
1143 .filter(col("number").lt(lit(10)))
1144 .unwrap();
1145 let batches = df.collect().await.unwrap();
1146 assert_eq!(1, batches.len());
1147 let batch = &batches[0];
1148
1149 assert_eq!(1, batch.num_columns());
1150 assert_eq!(batch.column(0).len(), 10);
1151
1152 assert_eq!(
1153 Helper::try_into_vector(batch.column(0)).unwrap(),
1154 Arc::new(UInt32Vector::from_slice([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) as VectorRef
1155 );
1156 }
1157
1158 #[tokio::test]
1159 async fn test_describe() {
1160 let engine = create_test_engine().await;
1161 let sql = "select sum(number) from numbers limit 20";
1162
1163 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1164
1165 let plan = engine
1166 .planner()
1167 .plan(&stmt, QueryContext::arc())
1168 .await
1169 .unwrap();
1170
1171 let DescribeResult { logical_plan } =
1172 engine.describe(plan, QueryContext::arc()).await.unwrap();
1173
1174 let schema: Schema = logical_plan.schema().clone().try_into().unwrap();
1175
1176 assert_eq!(
1177 schema.column_schemas()[0],
1178 ColumnSchema::new(
1179 "sum(numbers.number)",
1180 ConcreteDataType::uint64_datatype(),
1181 true
1182 )
1183 );
1184 assert_eq!(
1185 "Limit: skip=0, fetch=20\n Projection: sum(numbers.number)\n Aggregate: groupBy=[[]], aggr=[[sum(numbers.number)]]\n TableScan: numbers",
1186 format!("{}", logical_plan.display_indent())
1187 );
1188 }
1189
1190 #[tokio::test]
1191 async fn test_topk_dynamic_filter_pushdown_reaches_region_scan() {
1192 let engine = create_test_engine().await;
1193 let engine = engine
1194 .as_any()
1195 .downcast_ref::<DatafusionQueryEngine>()
1196 .unwrap();
1197 let engine_ctx = engine.engine_context(QueryContext::arc());
1198 let state = engine_ctx.state();
1199
1200 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1201 "ts",
1202 ConcreteDataType::timestamp_millisecond_datatype(),
1203 false,
1204 )]));
1205
1206 let mut metadata_builder = RegionMetadataBuilder::new(RegionId::new(1024, 1));
1207 metadata_builder
1208 .push_column_metadata(ColumnMetadata {
1209 column_schema: ColumnSchema::new(
1210 "ts",
1211 ConcreteDataType::timestamp_millisecond_datatype(),
1212 false,
1213 )
1214 .with_time_index(true),
1215 semantic_type: SemanticType::Timestamp,
1216 column_id: 1,
1217 })
1218 .primary_key(vec![]);
1219 let metadata = Arc::new(metadata_builder.build().unwrap());
1220
1221 let update_calls = Arc::new(AtomicUsize::new(0));
1222 let last_filter_len = Arc::new(AtomicUsize::new(0));
1223 let scanner = Box::new(RecordingScanner::new(
1224 schema,
1225 metadata,
1226 update_calls.clone(),
1227 last_filter_len.clone(),
1228 ));
1229 let scan = Arc::new(RegionScanExec::new(scanner, ScanRequest::default(), None).unwrap());
1230
1231 let sort_expr = PhysicalSortExpr {
1232 expr: Arc::new(Column::new("ts", 0)),
1233 options: SortOptions {
1234 descending: true,
1235 ..Default::default()
1236 },
1237 };
1238 let partition_ranges: Vec<Vec<PartitionRange>> = vec![vec![]];
1239 let mut plan: Arc<dyn ExecutionPlan> =
1240 Arc::new(PartSortExec::try_new(sort_expr, Some(3), partition_ranges, scan).unwrap());
1241
1242 for optimizer in state.physical_optimizers() {
1243 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1244 }
1245
1246 assert!(update_calls.load(Ordering::Relaxed) > 0);
1247 assert!(last_filter_len.load(Ordering::Relaxed) > 0);
1248 }
1249
1250 #[tokio::test]
1251 async fn test_join_dynamic_filter_pushdown_reaches_region_scan() {
1252 let engine = create_test_engine().await;
1253 let engine = engine
1254 .as_any()
1255 .downcast_ref::<DatafusionQueryEngine>()
1256 .unwrap();
1257 let engine_ctx = engine.engine_context(QueryContext::arc());
1258 let state = engine_ctx.state();
1259
1260 assert!(
1261 state
1262 .config_options()
1263 .optimizer
1264 .enable_join_dynamic_filter_pushdown
1265 );
1266
1267 let schema = Arc::new(datatypes::schema::Schema::new(vec![ColumnSchema::new(
1268 "ts",
1269 ConcreteDataType::timestamp_millisecond_datatype(),
1270 false,
1271 )]));
1272
1273 let mut left_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 1));
1274 left_metadata_builder
1275 .push_column_metadata(ColumnMetadata {
1276 column_schema: ColumnSchema::new(
1277 "ts",
1278 ConcreteDataType::timestamp_millisecond_datatype(),
1279 false,
1280 )
1281 .with_time_index(true),
1282 semantic_type: SemanticType::Timestamp,
1283 column_id: 1,
1284 })
1285 .primary_key(vec![]);
1286 let left_metadata = Arc::new(left_metadata_builder.build().unwrap());
1287
1288 let mut right_metadata_builder = RegionMetadataBuilder::new(RegionId::new(2048, 2));
1289 right_metadata_builder
1290 .push_column_metadata(ColumnMetadata {
1291 column_schema: ColumnSchema::new(
1292 "ts",
1293 ConcreteDataType::timestamp_millisecond_datatype(),
1294 false,
1295 )
1296 .with_time_index(true),
1297 semantic_type: SemanticType::Timestamp,
1298 column_id: 1,
1299 })
1300 .primary_key(vec![]);
1301 let right_metadata = Arc::new(right_metadata_builder.build().unwrap());
1302
1303 let left_update_calls = Arc::new(AtomicUsize::new(0));
1304 let left_last_filter_len = Arc::new(AtomicUsize::new(0));
1305 let right_update_calls = Arc::new(AtomicUsize::new(0));
1306 let right_last_filter_len = Arc::new(AtomicUsize::new(0));
1307
1308 let left_scan = Arc::new(
1309 RegionScanExec::new(
1310 Box::new(RecordingScanner::new(
1311 schema.clone(),
1312 left_metadata,
1313 left_update_calls.clone(),
1314 left_last_filter_len.clone(),
1315 )),
1316 ScanRequest::default(),
1317 None,
1318 )
1319 .unwrap(),
1320 );
1321 let right_scan = Arc::new(
1322 RegionScanExec::new(
1323 Box::new(RecordingScanner::new(
1324 schema,
1325 right_metadata,
1326 right_update_calls.clone(),
1327 right_last_filter_len.clone(),
1328 )),
1329 ScanRequest::default(),
1330 None,
1331 )
1332 .unwrap(),
1333 );
1334
1335 let on: JoinOn = vec![(
1336 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1337 Arc::new(Column::new("ts", 0)) as Arc<dyn PhysicalExpr>,
1338 )];
1339
1340 let mut plan: Arc<dyn ExecutionPlan> = Arc::new(
1341 HashJoinExec::try_new(
1342 left_scan,
1343 right_scan,
1344 on,
1345 None,
1346 &JoinType::Inner,
1347 None,
1348 PartitionMode::CollectLeft,
1349 NullEquality::NullEqualsNull,
1350 false,
1351 )
1352 .unwrap(),
1353 );
1354
1355 for optimizer in state.physical_optimizers() {
1356 plan = optimizer.optimize(plan, state.config_options()).unwrap();
1357 }
1358
1359 assert!(left_update_calls.load(Ordering::Relaxed) > 0);
1360 assert_eq!(0, left_last_filter_len.load(Ordering::Relaxed));
1361 assert!(right_update_calls.load(Ordering::Relaxed) > 0);
1362 assert!(right_last_filter_len.load(Ordering::Relaxed) > 0);
1363 }
1364 #[derive(Default)]
1365 struct RecordingMutationHandler {
1366 inserts: std::sync::Mutex<Vec<table::requests::InsertRequest>>,
1367 }
1368
1369 #[async_trait]
1370 impl common_function::handlers::TableMutationHandler for RecordingMutationHandler {
1371 async fn insert(
1372 &self,
1373 request: table::requests::InsertRequest,
1374 _ctx: session::context::QueryContextRef,
1375 ) -> common_query::error::Result<common_query::Output> {
1376 self.inserts.lock().unwrap().push(request);
1377 Ok(common_query::Output::new_with_affected_rows(1))
1378 }
1379
1380 async fn delete(
1381 &self,
1382 _request: table::requests::DeleteRequest,
1383 _ctx: session::context::QueryContextRef,
1384 ) -> common_query::error::Result<common_base::AffectedRows> {
1385 unimplemented!("unexpected delete")
1386 }
1387
1388 async fn flush(
1389 &self,
1390 _request: table::requests::FlushTableRequest,
1391 _ctx: session::context::QueryContextRef,
1392 ) -> common_query::error::Result<common_base::AffectedRows> {
1393 unimplemented!("unexpected flush")
1394 }
1395
1396 async fn compact(
1397 &self,
1398 _request: table::requests::CompactTableRequest,
1399 _ctx: session::context::QueryContextRef,
1400 ) -> common_query::error::Result<common_base::AffectedRows> {
1401 unimplemented!("unexpected compact")
1402 }
1403
1404 async fn build_index(
1405 &self,
1406 _request: table::requests::BuildIndexTableRequest,
1407 _ctx: session::context::QueryContextRef,
1408 ) -> common_query::error::Result<common_base::AffectedRows> {
1409 unimplemented!("unexpected build_index")
1410 }
1411
1412 async fn flush_region(
1413 &self,
1414 _region_id: store_api::storage::RegionId,
1415 _ctx: session::context::QueryContextRef,
1416 ) -> common_query::error::Result<common_base::AffectedRows> {
1417 unimplemented!("unexpected flush_region")
1418 }
1419
1420 async fn compact_region(
1421 &self,
1422 _region_id: store_api::storage::RegionId,
1423 _ctx: session::context::QueryContextRef,
1424 ) -> common_query::error::Result<common_base::AffectedRows> {
1425 unimplemented!("unexpected compact_region")
1426 }
1427
1428 async fn discard_unflushed_data(
1429 &self,
1430 _region_id: store_api::storage::RegionId,
1431 _ctx: session::context::QueryContextRef,
1432 ) -> common_query::error::Result<common_base::AffectedRows> {
1433 unimplemented!("unexpected discard_unflushed_data")
1434 }
1435
1436 async fn discard_unflushed_data_by_table(
1437 &self,
1438 _table_name: table::table_name::TableName,
1439 _ctx: session::context::QueryContextRef,
1440 ) -> common_query::error::Result<common_base::AffectedRows> {
1441 unimplemented!("unexpected discard_unflushed_data_by_table")
1442 }
1443 }
1444
1445 fn native_schema() -> Arc<Schema> {
1446 Arc::new(Schema::new(vec![
1447 ColumnSchema::new("dim", ConcreteDataType::date_datatype(), true),
1448 ColumnSchema::new("amount", ConcreteDataType::decimal128_datatype(30, 2), true),
1449 ColumnSchema::new(
1450 "elapsed",
1451 ConcreteDataType::duration_millisecond_datatype(),
1452 true,
1453 ),
1454 ColumnSchema::new(
1455 "ts",
1456 ConcreteDataType::timestamp_millisecond_datatype(),
1457 false,
1458 )
1459 .with_time_index(true),
1460 ColumnSchema::new(
1461 "updated_at",
1462 ConcreteDataType::timestamp_millisecond_datatype(),
1463 true,
1464 ),
1465 ColumnSchema::new("marker", ConcreteDataType::uint8_datatype(), true),
1466 ColumnSchema::new("payload", ConcreteDataType::binary_datatype(), true),
1467 ColumnSchema::new("epoch", ConcreteDataType::uint64_datatype(), true),
1468 ]))
1469 }
1470
1471 fn register_native_tables(catalog: &catalog::memory::MemoryCatalogManager) {
1472 let schema = native_schema();
1473 let meta = TableMetaBuilder::empty()
1474 .schema(schema.clone())
1475 .primary_key_indices(vec![])
1476 .value_indices((0..schema.num_columns()).collect())
1477 .next_column_id(8)
1478 .build()
1479 .unwrap();
1480 let info = TableInfoBuilder::default()
1481 .name("native_regression")
1482 .table_id(9001)
1483 .table_version(0)
1484 .meta(meta)
1485 .build()
1486 .unwrap();
1487 catalog
1488 .register_table_sync(RegisterTableRequest {
1489 catalog: DEFAULT_CATALOG_NAME.to_string(),
1490 schema: DEFAULT_SCHEMA_NAME.to_string(),
1491 table_name: "native_regression".to_string(),
1492 table_id: 9001,
1493 table: table::test_util::EmptyTable::from_table_info(&info),
1494 })
1495 .unwrap();
1496
1497 let schema = Arc::new(Schema::new(vec![
1498 ColumnSchema::new("dim", ConcreteDataType::date_datatype(), true),
1499 ColumnSchema::new("amount", ConcreteDataType::decimal128_datatype(20, 2), true),
1500 ColumnSchema::new(
1501 "elapsed",
1502 ConcreteDataType::duration_millisecond_datatype(),
1503 true,
1504 ),
1505 ColumnSchema::new(
1506 "ts",
1507 ConcreteDataType::timestamp_millisecond_datatype(),
1508 false,
1509 )
1510 .with_time_index(true),
1511 ]));
1512 let rows = RecordBatch::new(
1513 schema,
1514 vec![
1515 Arc::new(datatypes::vectors::DateVector::from_slice([0, 2])) as VectorRef,
1516 Arc::new(
1517 datatypes::vectors::Decimal128Vector::from_slice([10000, 20000])
1518 .with_precision_and_scale(20, 2)
1519 .unwrap(),
1520 ) as VectorRef,
1521 Arc::new(datatypes::vectors::DurationMillisecondVector::from_values(
1522 [10, 20],
1523 )) as VectorRef,
1524 Arc::new(datatypes::vectors::TimestampMillisecondVector::from_slice(
1525 [1, 2],
1526 )) as VectorRef,
1527 ],
1528 )
1529 .unwrap();
1530 catalog
1531 .register_table_sync(RegisterTableRequest {
1532 catalog: DEFAULT_CATALOG_NAME.to_string(),
1533 schema: DEFAULT_SCHEMA_NAME.to_string(),
1534 table_name: "native_aggregate".to_string(),
1535 table_id: 9002,
1536 table: table::test_util::MemTable::table("native_aggregate", rows),
1537 })
1538 .unwrap();
1539 }
1540
1541 async fn run_sql(engine: &QueryEngineRef, sql: &str) -> Vec<RecordBatch> {
1542 let stmt = QueryLanguageParser::parse_sql(sql, &QueryContext::arc()).unwrap();
1543 let plan = engine
1544 .planner()
1545 .plan(&stmt, QueryContext::arc())
1546 .await
1547 .unwrap();
1548 match engine
1549 .execute(plan, QueryContext::arc())
1550 .await
1551 .unwrap()
1552 .data
1553 {
1554 OutputData::Stream(stream) => util::collect(stream).await.unwrap(),
1555 _ => unreachable!(),
1556 }
1557 }
1558
1559 #[tokio::test]
1560 async fn test_native_executor_null_insert_and_aggregates() {
1561 let catalog = catalog::memory::new_memory_catalog_manager().unwrap();
1562 register_native_tables(&catalog);
1563 let handler = Arc::new(RecordingMutationHandler::default());
1564 let engine = QueryEngineFactory::new(
1565 catalog,
1566 None,
1567 Some(handler.clone()),
1568 None,
1569 None,
1570 false,
1571 QueryOptions::default(),
1572 )
1573 .query_engine();
1574
1575 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))";
1578 let stmt = QueryLanguageParser::parse_sql(insert_sql, &QueryContext::arc()).unwrap();
1579 let QueryStatement::Sql(sql::statements::statement::Statement::Insert(insert)) = &stmt
1580 else {
1581 unreachable!()
1582 };
1583 assert!(!insert.can_extract_values());
1584 let plan = engine
1585 .planner()
1586 .plan(&stmt, QueryContext::arc())
1587 .await
1588 .unwrap();
1589 assert!(matches!(
1590 engine
1591 .execute(plan, QueryContext::arc())
1592 .await
1593 .unwrap()
1594 .data,
1595 OutputData::AffectedRows(1)
1596 ));
1597
1598 let request = handler.inserts.lock().unwrap().pop().unwrap();
1599 for (name, data_type) in [
1600 ("dim", ConcreteDataType::date_datatype()),
1601 ("amount", ConcreteDataType::decimal128_datatype(30, 2)),
1602 ("elapsed", ConcreteDataType::duration_millisecond_datatype()),
1603 (
1604 "updated_at",
1605 ConcreteDataType::timestamp_millisecond_datatype(),
1606 ),
1607 ] {
1608 let vector = request.columns_values.get(name).unwrap();
1609 assert_eq!(vector.len(), 1, "{name}");
1610 assert_eq!(vector.data_type(), data_type, "{name}");
1611 assert!(vector.is_null(0), "{name}");
1612 }
1613 assert!(!request.columns_values["ts"].is_null(0));
1614 for (name, data_type) in [
1615 ("marker", ConcreteDataType::uint8_datatype()),
1616 ("payload", ConcreteDataType::binary_datatype()),
1617 ("epoch", ConcreteDataType::uint64_datatype()),
1618 ] {
1619 assert_eq!(
1620 request.columns_values[name].data_type(),
1621 data_type,
1622 "{name}"
1623 );
1624 }
1625
1626 let batches = run_sql(
1627 &engine,
1628 "SELECT MIN(dim), MAX(dim), SUM(amount), SUM(elapsed) FROM native_aggregate",
1629 )
1630 .await;
1631 let batch = &batches[0];
1632 assert_eq!(batch.num_rows(), 1);
1633 for (index, (data_type, value)) in [
1634 (
1635 ConcreteDataType::date_datatype(),
1636 ScalarValue::Date32(Some(0)),
1637 ),
1638 (
1639 ConcreteDataType::date_datatype(),
1640 ScalarValue::Date32(Some(2)),
1641 ),
1642 (
1643 ConcreteDataType::decimal128_datatype(30, 2),
1644 ScalarValue::Decimal128(Some(30000), 30, 2),
1645 ),
1646 (
1647 ConcreteDataType::duration_millisecond_datatype(),
1648 ScalarValue::DurationMillisecond(Some(30)),
1649 ),
1650 ]
1651 .into_iter()
1652 .enumerate()
1653 {
1654 assert_eq!(batch.schema.column_schemas()[index].data_type, data_type);
1655 assert_eq!(
1656 ScalarValue::try_from_array(batch.column(index).as_ref(), 0).unwrap(),
1657 value
1658 );
1659 }
1660
1661 let batches = run_sql(
1662 &engine,
1663 "SELECT aggregate.amount_sum + aggregate.amount_sum AS amount_add, \
1664 aggregate.elapsed_sum + aggregate.elapsed_sum AS elapsed_add, \
1665 CASE WHEN aggregate.min_dim < CAST('1970-01-02' AS DATE) THEN true ELSE false END AS min_before, \
1666 CASE WHEN aggregate.max_dim > CAST('1970-01-01' AS DATE) THEN true ELSE false END AS max_after \
1667 FROM (SELECT MIN(dim) AS min_dim, MAX(dim) AS max_dim, SUM(amount) AS amount_sum, SUM(elapsed) AS elapsed_sum \
1668 FROM native_aggregate) AS aggregate",
1669 )
1670 .await;
1671 let batch = &batches[0];
1672 assert_eq!(batch.num_rows(), 1);
1673 for (index, (data_type, value)) in [
1674 (
1675 ConcreteDataType::decimal128_datatype(31, 2),
1676 ScalarValue::Decimal128(Some(60000), 31, 2),
1677 ),
1678 (
1679 ConcreteDataType::duration_millisecond_datatype(),
1680 ScalarValue::DurationMillisecond(Some(60)),
1681 ),
1682 (
1683 ConcreteDataType::boolean_datatype(),
1684 ScalarValue::Boolean(Some(true)),
1685 ),
1686 (
1687 ConcreteDataType::boolean_datatype(),
1688 ScalarValue::Boolean(Some(true)),
1689 ),
1690 ]
1691 .into_iter()
1692 .enumerate()
1693 {
1694 assert_eq!(batch.schema.column_schemas()[index].data_type, data_type);
1695 assert_eq!(
1696 ScalarValue::try_from_array(batch.column(index).as_ref(), 0).unwrap(),
1697 value
1698 );
1699 }
1700 }
1701}