1use std::sync::Arc;
16
17use bytes::Bytes;
18use catalog::CatalogManagerRef;
19use common_error::ext::BoxedError;
20use common_function::function::FunctionContext;
21use common_function::function_registry::FUNCTION_REGISTRY;
22use common_query::error::RegisterUdfSnafu;
23use common_query::logical_plan::SubstraitPlanDecoder;
24use datafusion::catalog::{CatalogProviderList, TableProvider};
25use datafusion::common::{DFSchema, DataFusionError, TableReference, not_impl_err, substrait_err};
26use datafusion::error::Result;
27use datafusion::execution::context::SessionState;
28use datafusion::execution::registry::SerializerRegistry;
29use datafusion::execution::{FunctionRegistry, SessionStateBuilder};
30use datafusion::logical_expr::{Extension, LogicalPlan};
31use datafusion_expr::{Expr, UserDefinedLogicalNode};
32use greptime_proto::substrait_extension::MergeScan as PbMergeScan;
33use promql::functions::register_promql_functions;
34use prost::Message;
35use session::context::{QueryContext, QueryContextRef};
36use snafu::ResultExt;
37use substrait::df_logical_plan::consumer::{DefaultSubstraitConsumer, SubstraitConsumer};
38use substrait::error::{DecodeDfPlanSnafu, DecodeRelSnafu};
39use substrait::extension_serializer::ExtensionSerializer;
40use substrait::substrait_proto_df::proto::{
41 ExtensionLeafRel, ExtensionMultiRel, ExtensionSingleRel, Plan, Type,
42};
43use substrait::{DFLogicalSubstraitConvertor, Extensions, SubstraitPlan};
44
45use crate::dist_plan::MergeScanLogicalPlan;
46use crate::query_engine::QueryEngineState;
47
48#[derive(Debug)]
50pub struct DefaultSerializer;
51
52impl SerializerRegistry for DefaultSerializer {
53 fn serialize_logical_plan(&self, node: &dyn UserDefinedLogicalNode) -> Result<Vec<u8>> {
54 if node.name() == MergeScanLogicalPlan::name() {
55 let merge_scan = node
56 .as_any()
57 .downcast_ref::<MergeScanLogicalPlan>()
58 .expect("Failed to downcast to MergeScanLogicalPlan");
59
60 let input = merge_scan.input();
61 let is_placeholder = merge_scan.is_placeholder();
62 let input = DFLogicalSubstraitConvertor
63 .encode(input, DefaultSerializer)
64 .map_err(|e| DataFusionError::External(Box::new(e)))?
65 .to_vec();
66
67 Ok(PbMergeScan {
68 is_placeholder,
69 input,
70 }
71 .encode_to_vec())
72 } else {
73 ExtensionSerializer.serialize_logical_plan(node)
74 }
75 }
76
77 fn deserialize_logical_plan(
78 &self,
79 name: &str,
80 bytes: &[u8],
81 ) -> Result<Arc<dyn UserDefinedLogicalNode>> {
82 if name == MergeScanLogicalPlan::name() {
83 Err(DataFusionError::Substrait(format!(
85 "Unsupported plan node: {name}"
86 )))
87 } else {
88 ExtensionSerializer.deserialize_logical_plan(name, bytes)
89 }
90 }
91}
92
93struct MergeScanSubstraitConsumer<'a> {
95 inner: DefaultSubstraitConsumer<'a>,
96 session_state: &'a SessionState,
97 catalog_manager: Option<CatalogManagerRef>,
98 payload: bool,
99}
100
101impl MergeScanSubstraitConsumer<'_> {
102 fn query_ctx(&self) -> QueryContextRef {
103 self.session_state
104 .config()
105 .get_extension()
106 .unwrap_or_else(QueryContext::arc)
107 }
108
109 async fn decode_payload(&self, payload: Vec<u8>) -> Result<LogicalPlan> {
110 decode_plan(Bytes::from(payload), self.session_state, true)
111 .await
112 .map_err(|e| DataFusionError::External(Box::new(e)))
113 }
114}
115
116#[async_trait::async_trait]
117impl SubstraitConsumer for MergeScanSubstraitConsumer<'_> {
118 async fn resolve_table_ref(
119 &self,
120 table_ref: &TableReference,
121 ) -> Result<Option<Arc<dyn TableProvider>>> {
122 if !self.payload {
123 return self.inner.resolve_table_ref(table_ref).await;
124 }
125
126 let query_ctx = self.query_ctx();
127 let resolved = table_ref
128 .clone()
129 .resolve(query_ctx.current_catalog(), &query_ctx.current_schema());
130 let Some(manager) = &self.catalog_manager else {
132 return self
133 .inner
134 .resolve_table_ref(&TableReference::from(resolved))
135 .await;
136 };
137 let catalog_list: Arc<dyn CatalogProviderList> = Arc::new(
138 catalog::table_source::dummy_catalog::DummyCatalogList::new_with_query_ctx(
139 manager.clone(),
140 query_ctx,
141 ),
142 );
143 let schema_name = resolved.schema.as_ref();
144 let catalog_name = resolved.catalog.as_ref();
145 let schema = catalog_list
146 .catalog(catalog_name)
147 .ok_or_else(|| {
148 DataFusionError::Plan(format!("failed to resolve catalog: {catalog_name}"))
149 })?
150 .schema(schema_name)
151 .ok_or_else(|| {
152 DataFusionError::Plan(format!("failed to resolve schema: {schema_name}"))
153 })?;
154 schema.table(&resolved.table).await
155 }
156
157 fn get_extensions(&self) -> &Extensions {
158 self.inner.get_extensions()
159 }
160
161 fn get_function_registry(&self) -> &impl FunctionRegistry {
162 self.inner.get_function_registry()
163 }
164
165 fn push_outer_schema(&self, schema: Arc<DFSchema>) {
166 self.inner.push_outer_schema(schema)
167 }
168
169 fn pop_outer_schema(&self) {
170 self.inner.pop_outer_schema()
171 }
172
173 fn get_outer_schema(&self, steps_out: usize) -> Option<Arc<DFSchema>> {
174 self.inner.get_outer_schema(steps_out)
175 }
176
177 fn push_lambda_parameters(
178 &self,
179 lambda_parameters: &[Type],
180 input_schema: &DFSchema,
181 ) -> Result<Vec<String>> {
182 self.inner
183 .push_lambda_parameters(lambda_parameters, input_schema)
184 }
185
186 fn pop_lambda_parameters(&self) {
187 self.inner.pop_lambda_parameters()
188 }
189
190 fn lambda_variable(&self, steps_out: usize, field_idx: usize) -> Result<Expr> {
191 self.inner.lambda_variable(steps_out, field_idx)
192 }
193
194 async fn consume_extension_leaf(&self, rel: &ExtensionLeafRel) -> Result<LogicalPlan> {
195 let Some(detail) = rel.detail.as_ref() else {
197 return self.inner.consume_extension_leaf(rel).await;
198 };
199 if detail.type_url != MergeScanLogicalPlan::name() {
200 return self.inner.consume_extension_leaf(rel).await;
201 }
202
203 let merge_scan = PbMergeScan::decode(detail.value.as_ref()).map_err(|e| {
204 DataFusionError::Substrait(format!("Failed to decode the MergeScan plan node: {e}"))
205 })?;
206
207 let input = self.decode_payload(merge_scan.input).await?;
208
209 Ok(MergeScanLogicalPlan::new(input, merge_scan.is_placeholder).into_logical_plan())
210 }
211
212 async fn consume_extension_single(&self, rel: &ExtensionSingleRel) -> Result<LogicalPlan> {
214 let Some(detail) = &rel.detail else {
215 return substrait_err!("Unexpected empty detail in ExtensionSingleRel");
216 };
217 let plan = self
218 .session_state
219 .serializer_registry()
220 .deserialize_logical_plan(&detail.type_url, &detail.value)?;
221 let Some(input_rel) = &rel.input else {
222 return substrait_err!(
223 "ExtensionSingleRel missing input rel, try using ExtensionLeafRel instead"
224 );
225 };
226 let input_plan = self.consume_rel(input_rel).await?;
227 let plan = plan.with_exprs_and_inputs(plan.expressions(), vec![input_plan])?;
228 Ok(LogicalPlan::Extension(Extension { node: plan }))
229 }
230
231 async fn consume_extension_multi(&self, rel: &ExtensionMultiRel) -> Result<LogicalPlan> {
233 let Some(detail) = &rel.detail else {
234 return substrait_err!("Unexpected empty detail in ExtensionMultiRel");
235 };
236 let plan = self
237 .session_state
238 .serializer_registry()
239 .deserialize_logical_plan(&detail.type_url, &detail.value)?;
240 let mut inputs = Vec::with_capacity(rel.inputs.len());
241 for input in &rel.inputs {
242 let input_plan = self.consume_rel(input).await?;
243 inputs.push(input_plan);
244 }
245 let plan = plan.with_exprs_and_inputs(plan.expressions(), inputs)?;
246 Ok(LogicalPlan::Extension(Extension { node: plan }))
247 }
248}
249
250async fn decode_plan(
252 bytes: Bytes,
253 state: &SessionState,
254 payload: bool,
255) -> substrait::error::Result<LogicalPlan> {
256 let plan = Plan::decode(bytes).context(DecodeRelSnafu)?;
257 let extensions = Extensions::try_from(&plan.extensions).context(DecodeDfPlanSnafu)?;
258 if !extensions.type_variations.is_empty() {
259 return not_impl_err!("Type variation extensions are not supported")
260 .context(DecodeDfPlanSnafu);
261 }
262
263 let catalog_manager = state
264 .config()
265 .get_extension::<QueryEngineState>()
266 .map(|engine_state| engine_state.catalog_manager().clone());
267 let consumer = MergeScanSubstraitConsumer {
268 inner: DefaultSubstraitConsumer::new(&extensions, state),
269 session_state: state,
270 catalog_manager,
271 payload,
272 };
273
274 DFLogicalSubstraitConvertor
275 .decode_with_consumer(&plan, state, &consumer)
276 .await
277}
278
279pub struct DefaultPlanDecoder {
281 session_state: SessionState,
282 query_ctx: QueryContextRef,
283}
284
285impl DefaultPlanDecoder {
286 pub fn new(
287 session_state: SessionState,
288 query_ctx: &QueryContextRef,
289 ) -> crate::error::Result<Self> {
290 Ok(Self {
291 session_state,
292 query_ctx: query_ctx.clone(),
293 })
294 }
295}
296
297fn register_greptime_functions(
302 session_state: &mut SessionState,
303 query_ctx: &QueryContextRef,
304) -> common_query::error::Result<()> {
305 for func in FUNCTION_REGISTRY.scalar_functions() {
306 let udf = func.provide(FunctionContext {
307 query_ctx: query_ctx.clone(),
308 state: Default::default(),
309 });
310 session_state
311 .register_udf(Arc::new(udf))
312 .context(RegisterUdfSnafu { name: func.name() })?;
313 }
314
315 for func in FUNCTION_REGISTRY.aggregate_functions() {
316 let name = func.name().to_string();
317 session_state
318 .register_udaf(Arc::new(func))
319 .context(RegisterUdfSnafu { name })?;
320 }
321
322 register_promql_functions(session_state);
323
324 Ok(())
325}
326
327#[async_trait::async_trait]
328impl SubstraitPlanDecoder for DefaultPlanDecoder {
329 async fn decode(
330 &self,
331 message: bytes::Bytes,
332 catalog_list: Arc<dyn CatalogProviderList>,
333 optimize: bool,
334 ) -> common_query::error::Result<LogicalPlan> {
335 let mut session_state = SessionStateBuilder::new_from_existing(self.session_state.clone())
336 .with_serializer_registry(Arc::new(DefaultSerializer))
337 .with_catalog_list(catalog_list)
338 .build();
339 register_greptime_functions(&mut session_state, &self.query_ctx)?;
341
342 let logical_plan = decode_plan(message, &session_state, false)
343 .await
344 .map_err(BoxedError::new)
345 .context(common_query::error::DecodePlanSnafu)?;
346
347 if optimize {
348 self.session_state
349 .optimize(&logical_plan)
350 .map_err(Into::into)
351 } else {
352 Ok(logical_plan)
353 }
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use std::any::Any;
360 use std::sync::atomic::{AtomicBool, Ordering};
361
362 use catalog::error::Result as CatalogResult;
363 use catalog::{CatalogManager, RegisterTableRequest};
364 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, NUMBERS_TABLE_ID};
365 use common_function::aggrs::aggr_wrapper::StateWrapper;
366 use common_function::aggrs::aggr_wrapper::fix_order::FixStateUdafOrderingAnalyzer;
367 use common_query::logical_plan::SubstraitPlanDecoderRef;
368 use common_query::native_histogram::native_histogram_value_type;
369 use common_time::Timezone;
370 use datafusion::catalog::TableProvider;
371 use datafusion::datasource::MemTable;
372 use datafusion::logical_expr::Extension;
373 use datafusion::optimizer::AnalyzerRule;
374 use datafusion_expr::expr::{AggregateFunction, Cast, InSubquery, ScalarFunction, Sort};
375 use datafusion_expr::{
376 AggregateUDF, Expr, LogicalPlanBuilder, LogicalTableSource, ScalarUDF, Subquery, col, lit,
377 };
378 use datatypes::arrow::datatypes::{
379 DataType as ArrowDataType, Field, Schema, SchemaRef, TimeUnit,
380 };
381 use datatypes::data_type::DataType;
382 use futures::stream::BoxStream;
383 use promql::extension_plan::{RangeManipulate, SeriesNormalize, UnionDistinctOn};
384 use promql::functions::{
385 MixedRange, NativeHistogramAggAvg, NativeHistogramAggSum, NativeHistogramCount,
386 NativeHistogramDrop, PromqlFloatToString,
387 };
388 use session::context::QueryContext;
389 use table::TableRef;
390 use table::metadata::{TableId, TableInfoRef};
391 use table::table::numbers::{NUMBERS_TABLE_NAME, NumbersTable};
392
393 use super::*;
394 use crate::QueryEngineFactory;
395 use crate::dummy_catalog::DummyCatalogList;
396 use crate::optimizer::test_util::mock_table_provider;
397 use crate::options::QueryOptions;
398
399 fn mock_plan(schema: SchemaRef) -> LogicalPlan {
400 let table_source = LogicalTableSource::new(schema);
401 let projection = None;
402 let builder =
403 LogicalPlanBuilder::scan("devices", Arc::new(table_source), projection).unwrap();
404
405 builder
406 .filter(col("k0").eq(lit("hello")))
407 .unwrap()
408 .build()
409 .unwrap()
410 }
411
412 #[tokio::test]
413 async fn test_serializer_decode_plan() {
414 let catalog_list = catalog::memory::new_memory_catalog_manager().unwrap();
415 let factory = QueryEngineFactory::new(
416 catalog_list,
417 None,
418 None,
419 None,
420 None,
421 false,
422 QueryOptions::default(),
423 );
424
425 let engine = factory.query_engine();
426
427 let table_provider = Arc::new(mock_table_provider(1.into()));
428 let plan = mock_plan(table_provider.schema().clone());
429
430 let bytes = DFLogicalSubstraitConvertor
431 .encode(&plan, DefaultSerializer)
432 .unwrap();
433
434 let plan_decoder = engine
435 .engine_context(QueryContext::arc())
436 .new_plan_decoder()
437 .unwrap();
438 let catalog_list = Arc::new(DummyCatalogList::with_table_provider(table_provider));
439
440 let decode_plan = plan_decoder
441 .decode(bytes, catalog_list, false)
442 .await
443 .unwrap();
444
445 assert_eq!(
446 "Filter: devices.k0 = Utf8(\"hello\")
447 TableScan: devices",
448 decode_plan.to_string(),
449 );
450 }
451
452 #[tokio::test]
453 async fn test_serializer_decode_native_histogram_udf() {
454 let catalog_list = catalog::memory::new_memory_catalog_manager().unwrap();
455 let factory = QueryEngineFactory::new(
456 catalog_list,
457 None,
458 None,
459 None,
460 None,
461 false,
462 QueryOptions::default(),
463 );
464 let engine = factory.query_engine();
465 let schema = Arc::new(Schema::new(vec![Field::new(
466 "histogram",
467 native_histogram_value_type().as_arrow_type(),
468 true,
469 )]));
470 let plan = LogicalPlanBuilder::scan(
471 "devices",
472 Arc::new(LogicalTableSource::new(schema.clone())),
473 None,
474 )
475 .unwrap()
476 .aggregate(
477 Vec::<Expr>::new(),
478 vec![
479 Arc::new(NativeHistogramAggSum::aggregate_udf())
480 .call(vec![col("histogram")])
481 .alias("sum"),
482 Arc::new(NativeHistogramAggAvg::aggregate_udf())
483 .call(vec![col("histogram")])
484 .alias("avg"),
485 ],
486 )
487 .unwrap()
488 .project(vec![
489 Expr::ScalarFunction(ScalarFunction {
490 func: Arc::new(NativeHistogramCount::scalar_udf()),
491 args: vec![col("sum")],
492 }),
493 Expr::ScalarFunction(ScalarFunction {
494 func: Arc::new(NativeHistogramDrop::bool_false_udf(
495 "ignored annotation".to_string(),
496 None,
497 )),
498 args: vec![col("sum")],
499 }),
500 Expr::ScalarFunction(ScalarFunction {
501 func: Arc::new(NativeHistogramDrop::bool_true_udf(
502 "ignored annotation".to_string(),
503 None,
504 )),
505 args: vec![col("sum")],
506 }),
507 Expr::ScalarFunction(ScalarFunction {
508 func: Arc::new(NativeHistogramDrop::float_null_udf(
509 "ignored annotation".to_string(),
510 None,
511 )),
512 args: vec![col("sum")],
513 }),
514 Expr::ScalarFunction(ScalarFunction {
515 func: Arc::new(PromqlFloatToString::scalar_udf()),
516 args: vec![lit(2.0)],
517 }),
518 ])
519 .unwrap()
520 .build()
521 .unwrap();
522 let bytes = DFLogicalSubstraitConvertor
523 .encode(&plan, DefaultSerializer)
524 .unwrap();
525 let table_provider = Arc::new(MemTable::try_new(schema, vec![vec![]]).unwrap());
526 let plan_decoder = engine
527 .engine_context(QueryContext::arc())
528 .new_plan_decoder()
529 .unwrap();
530
531 let decoded = plan_decoder
532 .decode(
533 bytes,
534 Arc::new(DummyCatalogList::with_table_provider(table_provider)),
535 false,
536 )
537 .await
538 .unwrap();
539
540 let decoded = decoded.to_string();
541 assert!(decoded.contains("prom_native_histogram_count"));
542 assert!(decoded.contains("prom_native_histogram_drop_bool"));
543 assert!(decoded.contains("prom_native_histogram_keep_bool"));
544 assert!(decoded.contains("prom_native_histogram_drop_float"));
545 assert!(decoded.contains(NativeHistogramAggSum::name()));
546 assert!(decoded.contains(NativeHistogramAggAvg::name()));
547 assert!(decoded.contains(PromqlFloatToString::name()));
548
549 let schema = Arc::new(Schema::new(vec![
550 Field::new(
551 "timestamp",
552 ArrowDataType::Timestamp(TimeUnit::Millisecond, None),
553 false,
554 ),
555 Field::new("float", ArrowDataType::Float64, true),
556 Field::new(
557 "histogram",
558 native_histogram_value_type().as_arrow_type(),
559 true,
560 ),
561 ]));
562 let input = LogicalPlanBuilder::scan(
563 "devices",
564 Arc::new(LogicalTableSource::new(schema.clone())),
565 None,
566 )
567 .unwrap()
568 .build()
569 .unwrap();
570 let input = LogicalPlan::Extension(Extension {
571 node: Arc::new(
572 RangeManipulate::new(
573 0,
574 1000,
575 1000,
576 0,
577 1000,
578 "timestamp".to_string(),
579 vec!["float".to_string(), "histogram".to_string()],
580 input,
581 )
582 .unwrap(),
583 ),
584 });
585 let plan = LogicalPlanBuilder::from(input)
586 .project(vec![
587 Expr::ScalarFunction(ScalarFunction {
588 func: Arc::new(MixedRange::float_udf(None)),
589 args: vec![
590 lit("last_over_time"),
591 col("timestamp_range"),
592 col("float"),
593 col("histogram"),
594 ],
595 })
596 .alias("mixed_float"),
597 Expr::ScalarFunction(ScalarFunction {
598 func: Arc::new(MixedRange::histogram_udf(None)),
599 args: vec![
600 lit("last_over_time"),
601 col("timestamp_range"),
602 col("float"),
603 col("histogram"),
604 ],
605 })
606 .alias("mixed_histogram"),
607 ])
608 .unwrap()
609 .build()
610 .unwrap();
611 let bytes = DFLogicalSubstraitConvertor
612 .encode(&plan, DefaultSerializer)
613 .unwrap();
614 let table_provider = Arc::new(MemTable::try_new(schema, vec![vec![]]).unwrap());
615 let decoded = plan_decoder
616 .decode(
617 bytes,
618 Arc::new(DummyCatalogList::with_table_provider(table_provider)),
619 false,
620 )
621 .await
622 .unwrap()
623 .to_string();
624 assert!(decoded.contains("prom_mixed_range_float"));
625 assert!(decoded.contains("prom_mixed_range_histogram"));
626 }
627
628 #[tokio::test]
629 async fn test_serializer_decode_merge_scan() {
630 let engine = QueryEngineFactory::new(
631 numbers_catalog_manager(),
632 None,
633 None,
634 None,
635 None,
636 false,
637 QueryOptions::default(),
638 )
639 .query_engine();
640 let plan = merge_scan(numbers_scan(), true);
641 let decoded = plan_decoder(engine.as_ref())
642 .decode(encode_plan(&plan), request_catalog_list(), false)
643 .await
644 .unwrap();
645
646 let LogicalPlan::Extension(extension) = &decoded else {
647 panic!("Expect a MergeScan plan, got: {decoded}");
648 };
649 let merge_scan = extension
650 .node
651 .as_any()
652 .downcast_ref::<MergeScanLogicalPlan>()
653 .expect("Expect a MergeScan plan node");
654 assert!(merge_scan.is_placeholder());
655 assert_eq!(merge_scan.input().to_string(), numbers_scan().to_string());
656 }
657
658 #[tokio::test]
660 async fn test_serializer_decode_merge_scan_with_engine_catalog() {
661 let engine = QueryEngineFactory::new(
662 numbers_catalog_manager(),
663 None,
664 None,
665 None,
666 None,
667 false,
668 QueryOptions::default(),
669 )
670 .query_engine();
671 let decoded = plan_decoder(engine.as_ref())
672 .decode(
673 encode_plan(&merge_scan(numbers_scan(), false)),
674 request_catalog_list(),
675 false,
676 )
677 .await
678 .unwrap();
679
680 let LogicalPlan::Extension(extension) = &decoded else {
681 panic!("Expect a MergeScan plan, got: {decoded}");
682 };
683 let merge_scan = extension
684 .node
685 .as_any()
686 .downcast_ref::<MergeScanLogicalPlan>()
687 .expect("Expect a MergeScan plan node");
688 let LogicalPlan::TableScan(scan) = merge_scan.input() else {
689 panic!("Expect a table scan, got: {}", merge_scan.input());
690 };
691 assert_eq!(scan.table_name.table(), NUMBERS_TABLE_NAME);
692 assert_eq!(scan.source.schema().fields().len(), 1);
693 assert_eq!(scan.source.schema().field(0).name(), "number");
694 }
695
696 #[tokio::test]
698 async fn test_serializer_decode_nested_merge_scan() {
699 let engine = QueryEngineFactory::new(
700 numbers_catalog_manager(),
701 None,
702 None,
703 None,
704 None,
705 false,
706 QueryOptions::default(),
707 )
708 .query_engine();
709 let inner = merge_scan(numbers_scan(), false);
710 let input = LogicalPlanBuilder::from(inner)
711 .filter(col("number").lt(lit(10u32)))
712 .unwrap()
713 .build()
714 .unwrap();
715 let plan = merge_scan(input, false);
716
717 let decoded = plan_decoder(engine.as_ref())
718 .decode(encode_plan(&plan), request_catalog_list(), false)
719 .await
720 .unwrap();
721
722 assert_eq!(decoded.to_string(), plan.to_string());
723 assert_eq!(decoded.to_string().matches("MergeScan [").count(), 2);
724 }
725
726 fn greptime_date_format_udf(query_ctx: &QueryContextRef) -> Arc<ScalarUDF> {
727 FUNCTION_REGISTRY
728 .get_function("date_format")
729 .expect("`date_format` is a function of GreptimeDB")
730 .provide(FunctionContext {
731 query_ctx: query_ctx.clone(),
732 state: Default::default(),
733 })
734 .into()
735 }
736
737 fn date_format_query_ctx() -> QueryContextRef {
739 let query_ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
740 query_ctx.set_timezone(Timezone::from_tz_string("Asia/Shanghai").unwrap());
741 Arc::new(query_ctx)
742 }
743
744 fn greptime_date_format_expr(query_ctx: &QueryContextRef, timestamp: Expr) -> Expr {
745 Expr::ScalarFunction(ScalarFunction {
746 func: greptime_date_format_udf(query_ctx),
747 args: vec![timestamp, lit("%Y-%m-%d %H:%i:%S")],
748 })
749 }
750
751 fn scalar_functions_of(plan: &LogicalPlan) -> Vec<Arc<ScalarUDF>> {
753 use datafusion::common::tree_node::{TreeNode, TreeNodeRecursion};
754
755 let mut udfs = Vec::new();
756 for expr in plan.expressions() {
757 expr.apply(|expr| {
758 if let Expr::ScalarFunction(func) = expr {
759 udfs.push(Arc::clone(&func.func));
760 }
761 Ok(TreeNodeRecursion::Continue)
762 })
763 .unwrap();
764 }
765
766 if let LogicalPlan::Extension(extension) = plan
767 && let Some(merge_scan) = extension
768 .node
769 .as_any()
770 .downcast_ref::<MergeScanLogicalPlan>()
771 {
772 udfs.extend(scalar_functions_of(merge_scan.input()));
773 }
774
775 for input in plan.inputs() {
776 udfs.extend(scalar_functions_of(input));
777 }
778
779 udfs
780 }
781
782 fn assert_only_function_is_greptime_date_format(
784 plan: &LogicalPlan,
785 query_ctx: &QueryContextRef,
786 ) {
787 let udfs = scalar_functions_of(plan);
788 assert_eq!(udfs.len(), 1, "expected one function in: {plan}");
789
790 let udf = &udfs[0];
791 assert_eq!(udf.name(), "date_format", "got: {plan}");
792 assert_eq!(
793 udf.as_ref(),
794 greptime_date_format_udf(query_ctx).as_ref(),
795 "`date_format` is not bound to the implementation of GreptimeDB: {plan}"
796 );
797 }
798
799 #[tokio::test]
801 async fn test_serializer_decode_keeps_greptime_date_format() {
802 for _ in 0..8 {
804 let catalog_list = catalog::memory::new_memory_catalog_manager().unwrap();
805 let factory = QueryEngineFactory::new(
806 catalog_list,
807 None,
808 None,
809 None,
810 None,
811 false,
812 QueryOptions::default(),
813 );
814 let engine = factory.query_engine();
815
816 let table_provider = Arc::new(mock_table_provider(1.into()));
817 let query_ctx = date_format_query_ctx();
818 let plan = LogicalPlanBuilder::scan(
819 "devices",
820 Arc::new(LogicalTableSource::new(table_provider.schema().clone())),
821 None,
822 )
823 .unwrap()
824 .project(vec![greptime_date_format_expr(&query_ctx, col("ts"))])
825 .unwrap()
826 .build()
827 .unwrap();
828
829 let bytes = DFLogicalSubstraitConvertor
830 .encode(&plan, DefaultSerializer)
831 .unwrap();
832 let plan_decoder = engine
833 .engine_context(query_ctx.clone())
834 .new_plan_decoder()
835 .unwrap();
836 let decoded = plan_decoder
837 .decode(
838 bytes,
839 Arc::new(DummyCatalogList::with_table_provider(table_provider)),
840 false,
841 )
842 .await
843 .unwrap();
844
845 assert_only_function_is_greptime_date_format(&decoded, &query_ctx);
846 }
847 }
848
849 #[tokio::test]
851 async fn test_serializer_decode_merge_scan_payload_keeps_greptime_date_format() {
852 let catalog_manager = catalog::memory::new_memory_catalog_manager().unwrap();
853 catalog_manager
854 .register_table_sync(RegisterTableRequest {
855 catalog: DEFAULT_CATALOG_NAME.to_string(),
856 schema: DEFAULT_SCHEMA_NAME.to_string(),
857 table_name: NUMBERS_TABLE_NAME.to_string(),
858 table_id: NUMBERS_TABLE_ID,
859 table: NumbersTable::table(NUMBERS_TABLE_ID),
860 })
861 .unwrap();
862
863 for _ in 0..8 {
864 let factory = QueryEngineFactory::new(
865 catalog_manager.clone(),
866 None,
867 None,
868 None,
869 None,
870 false,
871 QueryOptions::default(),
872 );
873 let engine = factory.query_engine();
874 let query_ctx = date_format_query_ctx();
875
876 let input = LogicalPlanBuilder::scan(
877 NUMBERS_TABLE_NAME,
878 Arc::new(LogicalTableSource::new(
879 NumbersTable::schema().arrow_schema().clone(),
880 )),
881 None,
882 )
883 .unwrap()
884 .project(vec![greptime_date_format_expr(
885 &query_ctx,
886 Expr::Cast(Cast::new(
887 Box::new(col("number")),
888 ArrowDataType::Timestamp(TimeUnit::Millisecond, None),
889 )),
890 )])
891 .unwrap()
892 .build()
893 .unwrap();
894 let plan = MergeScanLogicalPlan::new(input, false).into_logical_plan();
895
896 let bytes = DFLogicalSubstraitConvertor
897 .encode(&plan, DefaultSerializer)
898 .unwrap();
899 let plan_decoder = engine
900 .engine_context(query_ctx.clone())
901 .new_plan_decoder()
902 .unwrap();
903 let catalog_list = Arc::new(DummyCatalogList::with_table_provider(Arc::new(
904 mock_table_provider(1.into()),
905 )));
906
907 let decoded = plan_decoder
908 .decode(bytes, catalog_list, false)
909 .await
910 .unwrap();
911
912 let LogicalPlan::Extension(extension) = &decoded else {
913 panic!("Expect a MergeScan plan, got: {decoded}");
914 };
915 let merge_scan = extension
916 .node
917 .as_any()
918 .downcast_ref::<MergeScanLogicalPlan>()
919 .expect("Expect a MergeScan plan node");
920
921 assert_only_function_is_greptime_date_format(merge_scan.input(), &query_ctx);
922 }
923 }
924
925 fn numbers_catalog_manager() -> CatalogManagerRef {
927 let catalog_manager = catalog::memory::new_memory_catalog_manager().unwrap();
928 catalog_manager
929 .register_table_sync(RegisterTableRequest {
930 catalog: DEFAULT_CATALOG_NAME.to_string(),
931 schema: DEFAULT_SCHEMA_NAME.to_string(),
932 table_name: NUMBERS_TABLE_NAME.to_string(),
933 table_id: NUMBERS_TABLE_ID,
934 table: NumbersTable::table(NUMBERS_TABLE_ID),
935 })
936 .unwrap();
937 catalog_manager
938 }
939
940 fn numbers_scan() -> LogicalPlan {
942 LogicalPlanBuilder::scan(
943 NUMBERS_TABLE_NAME,
944 Arc::new(LogicalTableSource::new(
945 NumbersTable::schema().arrow_schema().clone(),
946 )),
947 None,
948 )
949 .unwrap()
950 .build()
951 .unwrap()
952 }
953
954 fn merge_scan(input: LogicalPlan, is_placeholder: bool) -> LogicalPlan {
955 MergeScanLogicalPlan::new(input, is_placeholder).into_logical_plan()
956 }
957
958 fn encode_plan(plan: &LogicalPlan) -> Bytes {
959 DFLogicalSubstraitConvertor
960 .encode(plan, DefaultSerializer)
961 .unwrap()
962 }
963
964 fn plan_decoder(engine: &dyn crate::QueryEngine) -> SubstraitPlanDecoderRef {
965 engine
966 .engine_context(QueryContext::arc())
967 .new_plan_decoder()
968 .unwrap()
969 }
970
971 fn request_catalog_list() -> Arc<dyn CatalogProviderList> {
973 Arc::new(DummyCatalogList::with_table_provider(Arc::new(
974 mock_table_provider(1.into()),
975 )))
976 }
977
978 #[tokio::test]
980 async fn test_serializer_decode_payload_observes_catalog_and_query_context() {
981 let observed = Arc::new(std::sync::Mutex::new(Vec::new()));
982 let inner = catalog::memory::new_memory_catalog_manager().unwrap();
983 let manager: CatalogManagerRef = Arc::new(InterceptingCatalogManager {
984 inner,
985 resolution: TableResolution::ObserveContext(observed.clone()),
986 });
987 let mut resolver_ctx = QueryContext::arc();
988 Arc::make_mut(&mut resolver_ctx).set_current_catalog("request_cat");
989 Arc::make_mut(&mut resolver_ctx).set_current_schema("request_schema");
990 let engine = QueryEngineFactory::new(
991 manager,
992 None,
993 None,
994 None,
995 None,
996 false,
997 QueryOptions::default(),
998 )
999 .query_engine();
1000 let mut state = engine.engine_context(resolver_ctx.clone()).state().clone();
1001 state.config_mut().set_extension(resolver_ctx);
1002 let extensions = Extensions::default();
1003 let consumer = MergeScanSubstraitConsumer {
1004 inner: DefaultSubstraitConsumer::new(&extensions, &state),
1005 session_state: &state,
1006 catalog_manager: state
1007 .config()
1008 .get_extension::<QueryEngineState>()
1009 .map(|s| s.catalog_manager().clone()),
1010 payload: true,
1011 };
1012 for reference in [
1013 TableReference::bare(NUMBERS_TABLE_NAME),
1014 TableReference::partial("explicit_schema", NUMBERS_TABLE_NAME),
1015 TableReference::full(
1016 DEFAULT_CATALOG_NAME,
1017 DEFAULT_SCHEMA_NAME,
1018 NUMBERS_TABLE_NAME,
1019 ),
1020 ] {
1021 consumer
1022 .resolve_table_ref(&reference)
1023 .await
1024 .unwrap()
1025 .unwrap();
1026 }
1027 assert_eq!(
1028 *observed.lock().unwrap(),
1029 vec![
1030 [
1031 "request_cat".to_string(),
1032 "request_schema".to_string(),
1033 NUMBERS_TABLE_NAME.to_string(),
1034 "request_cat".to_string(),
1035 "request_schema".to_string(),
1036 ],
1037 [
1038 "request_cat".to_string(),
1039 "explicit_schema".to_string(),
1040 NUMBERS_TABLE_NAME.to_string(),
1041 "request_cat".to_string(),
1042 "request_schema".to_string(),
1043 ],
1044 [
1045 DEFAULT_CATALOG_NAME.to_string(),
1046 DEFAULT_SCHEMA_NAME.to_string(),
1047 NUMBERS_TABLE_NAME.to_string(),
1048 "request_cat".to_string(),
1049 "request_schema".to_string(),
1050 ],
1051 ],
1052 );
1053 }
1054
1055 enum TableResolution {
1057 ObserveContext(Arc<std::sync::Mutex<Vec<[String; 5]>>>),
1059 AwaitSignal {
1061 notify: Arc<tokio::sync::Notify>,
1062 entered: Arc<AtomicBool>,
1063 },
1064 }
1065
1066 struct InterceptingCatalogManager {
1068 inner: CatalogManagerRef,
1069 resolution: TableResolution,
1070 }
1071
1072 #[async_trait::async_trait]
1073 impl CatalogManager for InterceptingCatalogManager {
1074 fn as_any(&self) -> &dyn Any {
1075 self.inner.as_any()
1076 }
1077
1078 async fn catalog_names(&self) -> CatalogResult<Vec<String>> {
1079 self.inner.catalog_names().await
1080 }
1081
1082 async fn schema_names(
1083 &self,
1084 catalog: &str,
1085 query_ctx: Option<&QueryContext>,
1086 ) -> CatalogResult<Vec<String>> {
1087 self.inner.schema_names(catalog, query_ctx).await
1088 }
1089
1090 async fn table_names(
1091 &self,
1092 catalog: &str,
1093 schema: &str,
1094 query_ctx: Option<&QueryContext>,
1095 ) -> CatalogResult<Vec<String>> {
1096 self.inner.table_names(catalog, schema, query_ctx).await
1097 }
1098
1099 async fn catalog_exists(&self, catalog: &str) -> CatalogResult<bool> {
1100 self.inner.catalog_exists(catalog).await
1101 }
1102
1103 async fn schema_exists(
1104 &self,
1105 catalog: &str,
1106 schema: &str,
1107 query_ctx: Option<&QueryContext>,
1108 ) -> CatalogResult<bool> {
1109 self.inner.schema_exists(catalog, schema, query_ctx).await
1110 }
1111
1112 async fn table_exists(
1113 &self,
1114 catalog: &str,
1115 schema: &str,
1116 table: &str,
1117 query_ctx: Option<&QueryContext>,
1118 ) -> CatalogResult<bool> {
1119 self.inner
1120 .table_exists(catalog, schema, table, query_ctx)
1121 .await
1122 }
1123
1124 async fn table(
1125 &self,
1126 catalog: &str,
1127 schema: &str,
1128 table_name: &str,
1129 query_ctx: Option<&QueryContext>,
1130 ) -> CatalogResult<Option<TableRef>> {
1131 match &self.resolution {
1132 TableResolution::ObserveContext(observed) => {
1133 let query_ctx = query_ctx.unwrap();
1134 observed.lock().unwrap().push([
1135 catalog.to_string(),
1136 schema.to_string(),
1137 table_name.to_string(),
1138 query_ctx.current_catalog().to_string(),
1139 query_ctx.current_schema(),
1140 ]);
1141 Ok(Some(NumbersTable::table(NUMBERS_TABLE_ID)))
1142 }
1143
1144 TableResolution::AwaitSignal { notify, entered } => {
1145 entered.store(true, Ordering::SeqCst);
1146 notify.notified().await;
1147 self.inner
1148 .table(catalog, schema, table_name, query_ctx)
1149 .await
1150 }
1151 }
1152 }
1153
1154 async fn table_info_by_id(&self, table_id: TableId) -> CatalogResult<Option<TableInfoRef>> {
1155 self.inner.table_info_by_id(table_id).await
1156 }
1157
1158 async fn tables_by_ids(
1159 &self,
1160 catalog: &str,
1161 schema: &str,
1162 table_ids: &[TableId],
1163 ) -> CatalogResult<Vec<TableRef>> {
1164 self.inner.tables_by_ids(catalog, schema, table_ids).await
1165 }
1166
1167 fn tables<'a>(
1168 &'a self,
1169 catalog: &'a str,
1170 schema: &'a str,
1171 query_ctx: Option<&'a QueryContext>,
1172 ) -> BoxStream<'a, CatalogResult<TableRef>> {
1173 self.inner.tables(catalog, schema, query_ctx)
1174 }
1175 }
1176
1177 fn engine_with_intercepting_catalog(
1178 resolution: TableResolution,
1179 ) -> Arc<dyn crate::QueryEngine> {
1180 let catalog_manager: CatalogManagerRef = Arc::new(InterceptingCatalogManager {
1181 inner: numbers_catalog_manager(),
1182 resolution,
1183 });
1184 QueryEngineFactory::new(
1185 catalog_manager,
1186 None,
1187 None,
1188 None,
1189 None,
1190 false,
1191 QueryOptions::default(),
1192 )
1193 .query_engine()
1194 }
1195
1196 #[tokio::test(flavor = "current_thread")]
1201 async fn test_serializer_decode_merge_scan_awaits_caller_runtime_task() {
1202 let notify = Arc::new(tokio::sync::Notify::new());
1203 let entered = Arc::new(AtomicBool::new(false));
1204 let engine = engine_with_intercepting_catalog(TableResolution::AwaitSignal {
1205 notify: notify.clone(),
1206 entered: entered.clone(),
1207 });
1208 let plan = merge_scan(numbers_scan(), false);
1209 let bytes = encode_plan(&plan);
1210
1211 let decoder = plan_decoder(engine.as_ref());
1212 let mut decode = Box::pin(decoder.decode(bytes, request_catalog_list(), false));
1213 assert!(futures::poll!(decode.as_mut()).is_pending());
1215 assert!(entered.load(Ordering::SeqCst));
1216
1217 let signal = tokio::spawn(async move {
1218 notify.notify_waiters();
1219 });
1220 tokio::task::yield_now().await;
1221 let decoded = decode.await.unwrap();
1222 signal.await.unwrap();
1223
1224 assert_eq!(decoded.to_string(), plan.to_string());
1225 }
1226
1227 #[tokio::test]
1229 async fn test_serializer_decode_missing_engine_catalog_table() {
1230 let missing_manager: CatalogManagerRef =
1231 catalog::memory::new_memory_catalog_manager().unwrap();
1232 let engine = QueryEngineFactory::new(
1233 missing_manager,
1234 None,
1235 None,
1236 None,
1237 None,
1238 false,
1239 QueryOptions::default(),
1240 )
1241 .query_engine();
1242 let bytes = encode_plan(&merge_scan(numbers_scan(), false));
1243
1244 let err = plan_decoder(engine.as_ref())
1245 .decode(bytes, request_catalog_list(), false)
1246 .await
1247 .unwrap_err();
1248
1249 let err = format!("{err:?}");
1250 assert!(
1251 err.contains("Table not found: greptime.public.numbers"),
1252 "unexpected error: {err}"
1253 );
1254 }
1255
1256 #[tokio::test]
1258 async fn test_serializer_decode_manual_state_keeps_extension_registry() {
1259 let manager = catalog::memory::new_memory_catalog_manager().unwrap();
1260 manager.register_catalog_sync("manual_cat").unwrap();
1261 manager
1262 .register_schema_sync(catalog::RegisterSchemaRequest {
1263 catalog: "manual_cat".to_string(),
1264 schema: "manual_schema".to_string(),
1265 })
1266 .unwrap();
1267 manager
1268 .register_table_sync(RegisterTableRequest {
1269 catalog: "manual_cat".to_string(),
1270 schema: "manual_schema".to_string(),
1271 table_name: NUMBERS_TABLE_NAME.to_string(),
1272 table_id: NUMBERS_TABLE_ID,
1273 table: NumbersTable::table(NUMBERS_TABLE_ID),
1274 })
1275 .unwrap();
1276 let query_ctx = Arc::new(QueryContext::with("manual_cat", "manual_schema"));
1277 let mut config = datafusion::execution::context::SessionConfig::new()
1278 .with_information_schema(true)
1279 .with_default_catalog_and_schema("manual_cat", "manual_schema");
1280 config.set_extension(query_ctx.clone());
1281 let state = SessionStateBuilder::new()
1282 .with_default_features()
1283 .with_config(config)
1284 .build();
1285 let decoder = DefaultPlanDecoder::new(state, &query_ctx).unwrap();
1286 let plan = LogicalPlan::Extension(Extension {
1287 node: Arc::new(SeriesNormalize::new(
1288 0,
1289 "number",
1290 false,
1291 Vec::new(),
1292 merge_scan(numbers_scan(), false),
1293 )),
1294 });
1295 let request_catalog = Arc::new(
1296 catalog::table_source::dummy_catalog::DummyCatalogList::new_with_query_ctx(
1297 manager, query_ctx,
1298 ),
1299 );
1300 let decoded = decoder
1301 .decode(encode_plan(&plan), request_catalog, false)
1302 .await
1303 .unwrap();
1304
1305 assert_eq!(decoded.to_string(), plan.to_string());
1306 assert_eq!(decoded.schema().as_arrow(), plan.schema().as_arrow());
1307 }
1308
1309 #[tokio::test]
1311 async fn test_serializer_decode_merge_scan_under_series_normalize() {
1312 let outer = merge_scan(numbers_scan(), false);
1313 let plan = LogicalPlan::Extension(Extension {
1314 node: Arc::new(SeriesNormalize::new(
1315 0,
1316 "number",
1317 false,
1318 Vec::new(),
1319 outer.clone(),
1320 )),
1321 });
1322 let bytes = encode_plan(&plan);
1323
1324 let decoded = plan_decoder(
1325 QueryEngineFactory::new(
1326 numbers_catalog_manager(),
1327 None,
1328 None,
1329 None,
1330 None,
1331 false,
1332 QueryOptions::default(),
1333 )
1334 .query_engine()
1335 .as_ref(),
1336 )
1337 .decode(bytes, request_catalog_list(), false)
1338 .await
1339 .unwrap();
1340
1341 let LogicalPlan::Extension(extension) = &decoded else {
1342 panic!("Expect a SeriesNormalize plan, got: {decoded}");
1343 };
1344 let normalize = extension
1345 .node
1346 .as_any()
1347 .downcast_ref::<SeriesNormalize>()
1348 .expect("Expect a SeriesNormalize plan node");
1349 assert_eq!(normalize.inputs()[0].to_string(), outer.to_string());
1350 }
1351
1352 #[tokio::test]
1354 async fn test_serializer_decode_merge_scan_under_union_distinct_on() {
1355 let left = merge_scan(numbers_scan(), false);
1356 let right = merge_scan(numbers_scan(), true);
1357 let plan = LogicalPlan::Extension(Extension {
1358 node: Arc::new(
1359 UnionDistinctOn::try_new(left.clone(), right.clone(), vec![], 0).unwrap(),
1360 ),
1361 });
1362 let bytes = encode_plan(&plan);
1363
1364 let decoded = plan_decoder(
1365 QueryEngineFactory::new(
1366 numbers_catalog_manager(),
1367 None,
1368 None,
1369 None,
1370 None,
1371 false,
1372 QueryOptions::default(),
1373 )
1374 .query_engine()
1375 .as_ref(),
1376 )
1377 .decode(bytes, request_catalog_list(), false)
1378 .await
1379 .unwrap();
1380
1381 let LogicalPlan::Extension(extension) = &decoded else {
1382 panic!("Expect a UnionDistinctOn plan, got: {decoded}");
1383 };
1384 let union = extension
1385 .node
1386 .as_any()
1387 .downcast_ref::<UnionDistinctOn>()
1388 .expect("Expect a UnionDistinctOn plan node");
1389 assert_eq!(union.inputs()[0].to_string(), left.to_string());
1390 assert_eq!(union.inputs()[1].to_string(), right.to_string());
1391 }
1392
1393 #[tokio::test]
1398 async fn test_serializer_decode_merge_scan_inside_subquery() {
1399 let inner = merge_scan(numbers_scan(), false);
1400 let payload = LogicalPlanBuilder::from(numbers_scan())
1401 .filter(Expr::InSubquery(InSubquery::new(
1402 Box::new(col("number")),
1403 Subquery {
1404 subquery: Arc::new(inner.clone()),
1405 outer_ref_columns: vec![],
1406 spans: Default::default(),
1407 },
1408 false,
1409 )))
1410 .unwrap()
1411 .build()
1412 .unwrap();
1413 let plan = merge_scan(payload.clone(), false);
1414 let bytes = encode_plan(&plan);
1415
1416 let decoded = plan_decoder(
1417 QueryEngineFactory::new(
1418 numbers_catalog_manager(),
1419 None,
1420 None,
1421 None,
1422 None,
1423 false,
1424 QueryOptions::default(),
1425 )
1426 .query_engine()
1427 .as_ref(),
1428 )
1429 .decode(bytes, request_catalog_list(), false)
1430 .await
1431 .unwrap();
1432
1433 assert_eq!(decoded.to_string().matches("MergeScan [").count(), 2);
1434 assert!(
1435 decoded.to_string().contains("IN (<subquery>)"),
1436 "expect the decoded subquery filter, got: {decoded}"
1437 );
1438 }
1439
1440 #[tokio::test]
1442 async fn test_serializer_decode_payload_keeps_state_udaf_ordering() {
1443 let last_value = (*datafusion::functions_aggregate::first_last::last_value_udaf()).clone();
1444 let state_udaf = AggregateUDF::new_from_impl(StateWrapper::new(last_value).unwrap());
1445 let aggr_expr = Expr::AggregateFunction(AggregateFunction::new_udf(
1446 Arc::new(state_udaf),
1447 vec![col("number")],
1448 false,
1449 None,
1450 vec![Sort::new(col("number"), true, true)],
1451 None,
1452 ));
1453 let payload = LogicalPlanBuilder::from(numbers_scan())
1454 .aggregate(Vec::<Expr>::new(), vec![aggr_expr])
1455 .unwrap()
1456 .build()
1457 .unwrap();
1458 let bytes = encode_plan(&payload);
1459 let encoded = Plan::decode(bytes.clone()).unwrap();
1460 let extensions = Extensions::try_from(&encoded.extensions).unwrap();
1461 let catalog_manager = numbers_catalog_manager();
1462 let engine = QueryEngineFactory::new(
1463 catalog_manager.clone(),
1464 None,
1465 None,
1466 None,
1467 None,
1468 false,
1469 QueryOptions::default(),
1470 )
1471 .query_engine();
1472 let mut state = SessionStateBuilder::new_from_existing(
1473 engine.engine_context(QueryContext::arc()).state().clone(),
1474 )
1475 .with_serializer_registry(Arc::new(DefaultSerializer))
1476 .build();
1477 register_greptime_functions(&mut state, &QueryContext::arc()).unwrap();
1478 let consumer = MergeScanSubstraitConsumer {
1479 inner: DefaultSubstraitConsumer::new(&extensions, &state),
1480 session_state: &state,
1481 catalog_manager: Some(catalog_manager),
1482 payload: false,
1483 };
1484 let decoded = consumer.decode_payload(bytes.to_vec()).await.unwrap();
1485
1486 let fixed_payload = FixStateUdafOrderingAnalyzer {}
1487 .analyze(payload.clone(), &Default::default())
1488 .unwrap();
1489 assert_ne!(
1490 fixed_payload.schema().as_arrow(),
1491 payload.schema().as_arrow()
1492 );
1493 assert_eq!(
1494 decoded.schema().as_arrow(),
1495 fixed_payload.schema().as_arrow()
1496 );
1497 let LogicalPlan::Aggregate(aggregate) = &decoded else {
1498 panic!("Expected an aggregate payload, got: {decoded}");
1499 };
1500 let Expr::AggregateFunction(function) = &aggregate.aggr_expr[0] else {
1501 panic!("Expected a state aggregate, got: {:?}", aggregate.aggr_expr);
1502 };
1503 assert!(function.func.inner().is::<StateWrapper>());
1504 assert_eq!(function.params.order_by.len(), 1);
1505 let ArrowDataType::Struct(fields) = decoded.schema().field(0).data_type() else {
1506 panic!("Expected the ordered aggregate state type");
1507 };
1508 assert_eq!(fields.len(), 3);
1509 assert_eq!(fields[1].name(), "number");
1510 assert!(fields[1].is_nullable());
1511 }
1512
1513 #[tokio::test]
1515 async fn test_serializer_decode_rejects_type_variations() {
1516 use substrait::substrait_proto_df::proto::extensions::SimpleExtensionDeclaration;
1517 use substrait::substrait_proto_df::proto::extensions::simple_extension_declaration::{
1518 ExtensionTypeVariation, MappingType,
1519 };
1520
1521 let engine = QueryEngineFactory::new(
1522 numbers_catalog_manager(),
1523 None,
1524 None,
1525 None,
1526 None,
1527 false,
1528 QueryOptions::default(),
1529 )
1530 .query_engine();
1531 let encoded = encode_plan(&merge_scan(numbers_scan(), false));
1532 let mut substrait_plan = Plan::decode(encoded).unwrap();
1533 substrait_plan.extensions.push(SimpleExtensionDeclaration {
1534 mapping_type: Some(MappingType::ExtensionTypeVariation(
1535 ExtensionTypeVariation {
1536 extension_urn_reference: u32::MAX,
1537 type_variation_anchor: 1,
1538 name: "u!variation".to_string(),
1539 },
1540 )),
1541 });
1542 let bytes = Bytes::from(substrait_plan.encode_to_vec());
1543
1544 let err = plan_decoder(engine.as_ref())
1545 .decode(bytes, request_catalog_list(), false)
1546 .await
1547 .unwrap_err();
1548
1549 let err = format!("{err:?}");
1550 assert!(
1551 err.contains("Type variation extensions are not supported"),
1552 "unexpected error: {err}"
1553 );
1554 }
1555}