1use std::collections::BTreeSet;
21use std::fmt::Debug;
22use std::sync::Arc;
23
24use crate::{OptimizerConfig, OptimizerRule};
25
26use crate::optimizer::ApplyOrder;
27use crate::utils::NamePreserver;
28use datafusion_common::alias::AliasGenerator;
29
30use datafusion_common::cse::{CSE, CSEController, FoundCommonNodes};
31use datafusion_common::tree_node::{Transformed, TreeNode};
32use datafusion_common::{Column, DFSchema, DFSchemaRef, Result, qualified_name};
33use datafusion_expr::expr::{Alias, HigherOrderFunction, ScalarFunction};
34use datafusion_expr::logical_plan::{
35 Aggregate, Filter, LogicalPlan, Projection, Sort, Window,
36};
37use datafusion_expr::{
38 BinaryExpr, Case, Expr, ExpressionPlacement, Operator, SortExpr, col,
39};
40
41const CSE_PREFIX: &str = "__common_expr";
42
43#[derive(Debug)]
70pub struct CommonSubexprEliminate {}
71
72impl CommonSubexprEliminate {
73 pub fn new() -> Self {
74 Self {}
75 }
76
77 fn try_optimize_proj(
78 &self,
79 projection: Projection,
80 config: &dyn OptimizerConfig,
81 ) -> Result<Transformed<LogicalPlan>> {
82 let Projection {
83 expr,
84 input,
85 schema,
86 ..
87 } = projection;
88 let input = Arc::unwrap_or_clone(input);
89 self.try_unary_plan(expr, input, config)?
90 .map_data(|(new_expr, new_input)| {
91 Projection::try_new_with_schema(new_expr, Arc::new(new_input), schema)
92 .map(LogicalPlan::Projection)
93 })
94 }
95
96 fn try_optimize_sort(
97 &self,
98 sort: Sort,
99 config: &dyn OptimizerConfig,
100 ) -> Result<Transformed<LogicalPlan>> {
101 let Sort { expr, input, fetch } = sort;
102 let input = Arc::unwrap_or_clone(input);
103 let (sort_expressions, sort_params): (Vec<_>, Vec<(_, _)>) = expr
104 .into_iter()
105 .map(|sort| (sort.expr, (sort.asc, sort.nulls_first)))
106 .unzip();
107 let new_sort = self
108 .try_unary_plan(sort_expressions, input, config)?
109 .update_data(|(new_expr, new_input)| {
110 LogicalPlan::Sort(Sort {
111 expr: new_expr
112 .into_iter()
113 .zip(sort_params)
114 .map(|(expr, (asc, nulls_first))| SortExpr {
115 expr,
116 asc,
117 nulls_first,
118 })
119 .collect(),
120 input: Arc::new(new_input),
121 fetch,
122 })
123 });
124 Ok(new_sort)
125 }
126
127 fn try_optimize_filter(
128 &self,
129 filter: Filter,
130 config: &dyn OptimizerConfig,
131 ) -> Result<Transformed<LogicalPlan>> {
132 let Filter {
133 predicate, input, ..
134 } = filter;
135 let input = Arc::unwrap_or_clone(input);
136 let expr = vec![predicate];
137 self.try_unary_plan(expr, input, config)?
138 .map_data(|(mut new_expr, new_input)| {
139 assert_eq!(new_expr.len(), 1); let new_predicate = new_expr.pop().unwrap();
141 Filter::try_new(new_predicate, Arc::new(new_input))
142 .map(LogicalPlan::Filter)
143 })
144 }
145
146 fn try_optimize_window(
147 &self,
148 window: Window,
149 config: &dyn OptimizerConfig,
150 ) -> Result<Transformed<LogicalPlan>> {
151 let (window_expr_list, window_schemas, input) =
154 get_consecutive_window_exprs(window);
155
156 match CSE::new(ExprCSEController::new(
159 config.alias_generator().as_ref(),
160 ExprMask::Normal,
161 ))
162 .extract_common_nodes(window_expr_list)?
163 {
164 FoundCommonNodes::Yes {
168 common_nodes: common_exprs,
169 new_nodes_list: new_exprs_list,
170 original_nodes_list: original_exprs_list,
171 } => build_common_expr_project_plan(input, common_exprs).map(|new_input| {
172 Transformed::yes((new_exprs_list, new_input, Some(original_exprs_list)))
173 }),
174 FoundCommonNodes::No {
175 original_nodes_list: original_exprs_list,
176 } => Ok(Transformed::no((original_exprs_list, input, None))),
177 }?
178 .transform_data(|(new_window_expr_list, new_input, window_expr_list)| {
181 self.rewrite(new_input, config)?.map_data(|new_input| {
182 Ok((new_window_expr_list, new_input, window_expr_list))
183 })
184 })?
185 .map_data(|(new_window_expr_list, new_input, window_expr_list)| {
187 if let Some(window_expr_list) = window_expr_list {
196 let name_preserver = NamePreserver::new_for_projection();
197 let saved_names = window_expr_list
198 .iter()
199 .map(|exprs| {
200 exprs
201 .iter()
202 .map(|expr| name_preserver.save(expr))
203 .collect::<Vec<_>>()
204 })
205 .collect::<Vec<_>>();
206 new_window_expr_list.into_iter().zip(saved_names).try_rfold(
207 new_input,
208 |plan, (new_window_expr, saved_names)| {
209 let new_window_expr = new_window_expr
210 .into_iter()
211 .zip(saved_names)
212 .map(|(new_window_expr, saved_name)| {
213 saved_name.restore(new_window_expr)
214 })
215 .collect::<Vec<_>>();
216 Window::try_new(new_window_expr, Arc::new(plan))
217 .map(LogicalPlan::Window)
218 },
219 )
220 } else {
221 new_window_expr_list
222 .into_iter()
223 .zip(window_schemas)
224 .try_rfold(new_input, |plan, (new_window_expr, schema)| {
225 Window::try_new_with_schema(
226 new_window_expr,
227 Arc::new(plan),
228 schema,
229 )
230 .map(LogicalPlan::Window)
231 })
232 }
233 })
234 }
235
236 fn try_optimize_aggregate(
237 &self,
238 aggregate: Aggregate,
239 config: &dyn OptimizerConfig,
240 ) -> Result<Transformed<LogicalPlan>> {
241 let Aggregate {
242 group_expr,
243 aggr_expr,
244 input,
245 schema,
246 ..
247 } = aggregate;
248 let input = Arc::unwrap_or_clone(input);
249 match CSE::new(ExprCSEController::new(
251 config.alias_generator().as_ref(),
252 ExprMask::Normal,
253 ))
254 .extract_common_nodes(vec![group_expr, aggr_expr])?
255 {
256 FoundCommonNodes::Yes {
260 common_nodes: common_exprs,
261 new_nodes_list: mut new_exprs_list,
262 original_nodes_list: mut original_exprs_list,
263 } => {
264 let new_aggr_expr = new_exprs_list.pop().unwrap();
265 let new_group_expr = new_exprs_list.pop().unwrap();
266
267 build_common_expr_project_plan(input, common_exprs).map(|new_input| {
268 let aggr_expr = original_exprs_list.pop().unwrap();
269 Transformed::yes((
270 new_aggr_expr,
271 new_group_expr,
272 new_input,
273 Some(aggr_expr),
274 ))
275 })
276 }
277
278 FoundCommonNodes::No {
279 original_nodes_list: mut original_exprs_list,
280 } => {
281 let new_aggr_expr = original_exprs_list.pop().unwrap();
282 let new_group_expr = original_exprs_list.pop().unwrap();
283
284 Ok(Transformed::no((
285 new_aggr_expr,
286 new_group_expr,
287 input,
288 None,
289 )))
290 }
291 }?
292 .transform_data(|(new_aggr_expr, new_group_expr, new_input, aggr_expr)| {
295 self.rewrite(new_input, config)?.map_data(|new_input| {
296 Ok((
297 new_aggr_expr,
298 new_group_expr,
299 aggr_expr,
300 Arc::new(new_input),
301 ))
302 })
303 })?
304 .transform_data(
306 |(new_aggr_expr, new_group_expr, aggr_expr, new_input)| {
307 match CSE::new(ExprCSEController::new(
309 config.alias_generator().as_ref(),
310 ExprMask::NormalAndAggregates,
311 ))
312 .extract_common_nodes(vec![new_aggr_expr])?
313 {
314 FoundCommonNodes::Yes {
315 common_nodes: common_exprs,
316 new_nodes_list: mut new_exprs_list,
317 original_nodes_list: mut original_exprs_list,
318 } => {
319 let rewritten_aggr_expr = new_exprs_list.pop().unwrap();
320 let new_aggr_expr = original_exprs_list.pop().unwrap();
321 let saved_names = if let Some(aggr_expr) = aggr_expr {
322 let name_preserver = NamePreserver::new_for_projection();
323 aggr_expr
324 .iter()
325 .map(|expr| Some(name_preserver.save(expr)))
326 .collect::<Vec<_>>()
327 } else {
328 (0..new_aggr_expr.len()).map(|_| None).collect()
329 };
330
331 let mut agg_exprs = common_exprs
332 .into_iter()
333 .map(|(expr, expr_alias)| expr.alias(expr_alias))
334 .collect::<Vec<_>>();
335
336 let mut proj_exprs = vec![];
337 for expr in &new_group_expr {
338 extract_expressions(expr, &mut proj_exprs)
339 }
340 for ((expr_rewritten, expr_orig), saved_name) in
341 rewritten_aggr_expr
342 .into_iter()
343 .zip(new_aggr_expr)
344 .zip(saved_names)
345 {
346 if expr_rewritten == expr_orig {
347 let expr_rewritten = if let Some(saved_name) = saved_name
348 {
349 saved_name.restore(expr_rewritten)
350 } else {
351 expr_rewritten
352 };
353 if let Expr::Alias(Alias { expr, name, .. }) =
354 expr_rewritten
355 {
356 agg_exprs.push(expr.alias(&name));
357 proj_exprs
358 .push(Expr::Column(Column::from_name(name)));
359 } else {
360 let expr_alias =
361 config.alias_generator().next(CSE_PREFIX);
362 let (qualifier, field_name) =
363 expr_rewritten.qualified_name();
364 let out_name =
365 qualified_name(qualifier.as_ref(), &field_name);
366
367 agg_exprs.push(expr_rewritten.alias(&expr_alias));
368 proj_exprs.push(
369 Expr::Column(Column::from_name(expr_alias))
370 .alias(out_name),
371 );
372 }
373 } else {
374 proj_exprs.push(expr_rewritten);
375 }
376 }
377
378 let agg = LogicalPlan::Aggregate(Aggregate::try_new(
379 new_input,
380 new_group_expr,
381 agg_exprs,
382 )?);
383 Projection::try_new(proj_exprs, Arc::new(agg))
384 .map(|p| Transformed::yes(LogicalPlan::Projection(p)))
385 }
386
387 FoundCommonNodes::No {
390 original_nodes_list: mut original_exprs_list,
391 } => {
392 let rewritten_aggr_expr = original_exprs_list.pop().unwrap();
393
394 if let Some(aggr_expr) = aggr_expr {
405 let name_preserver = NamePreserver::new_for_projection();
406 let saved_names = aggr_expr
407 .iter()
408 .map(|expr| name_preserver.save(expr))
409 .collect::<Vec<_>>();
410 let new_aggr_expr = rewritten_aggr_expr
411 .into_iter()
412 .zip(saved_names)
413 .map(|(new_expr, saved_name)| {
414 saved_name.restore(new_expr)
415 })
416 .collect::<Vec<Expr>>();
417
418 Aggregate::try_new(new_input, new_group_expr, new_aggr_expr)
421 .map(LogicalPlan::Aggregate)
422 .map(Transformed::no)
423 } else {
424 Aggregate::try_new_with_schema(
425 new_input,
426 new_group_expr,
427 rewritten_aggr_expr,
428 schema,
429 )
430 .map(LogicalPlan::Aggregate)
431 .map(Transformed::no)
432 }
433 }
434 }
435 },
436 )
437 }
438
439 fn try_unary_plan(
454 &self,
455 exprs: Vec<Expr>,
456 input: LogicalPlan,
457 config: &dyn OptimizerConfig,
458 ) -> Result<Transformed<(Vec<Expr>, LogicalPlan)>> {
459 match CSE::new(ExprCSEController::new(
461 config.alias_generator().as_ref(),
462 ExprMask::Normal,
463 ))
464 .extract_common_nodes(vec![exprs])?
465 {
466 FoundCommonNodes::Yes {
467 common_nodes: common_exprs,
468 new_nodes_list: mut new_exprs_list,
469 original_nodes_list: _,
470 } => {
471 let new_exprs = new_exprs_list.pop().unwrap();
472 build_common_expr_project_plan(input, common_exprs)
473 .map(|new_input| Transformed::yes((new_exprs, new_input)))
474 }
475 FoundCommonNodes::No {
476 original_nodes_list: mut original_exprs_list,
477 } => {
478 let new_exprs = original_exprs_list.pop().unwrap();
479 Ok(Transformed::no((new_exprs, input)))
480 }
481 }?
482 .transform_data(|(new_exprs, new_input)| {
485 self.rewrite(new_input, config)?
486 .map_data(|new_input| Ok((new_exprs, new_input)))
487 })
488 }
489}
490
491fn get_consecutive_window_exprs(
523 window: Window,
524) -> (Vec<Vec<Expr>>, Vec<DFSchemaRef>, LogicalPlan) {
525 let mut window_expr_list = vec![];
526 let mut window_schemas = vec![];
527 let mut plan = LogicalPlan::Window(window);
528 while let LogicalPlan::Window(Window {
529 input,
530 window_expr,
531 schema,
532 }) = plan
533 {
534 window_expr_list.push(window_expr);
535 window_schemas.push(schema);
536
537 plan = Arc::unwrap_or_clone(input);
538 }
539 (window_expr_list, window_schemas, plan)
540}
541
542impl OptimizerRule for CommonSubexprEliminate {
543 fn supports_rewrite(&self) -> bool {
544 true
545 }
546
547 fn apply_order(&self) -> Option<ApplyOrder> {
548 None
552 }
553
554 #[cfg_attr(feature = "recursive_protection", recursive::recursive)]
555 fn rewrite(
556 &self,
557 plan: LogicalPlan,
558 config: &dyn OptimizerConfig,
559 ) -> Result<Transformed<LogicalPlan>> {
560 let original_schema = Arc::clone(plan.schema());
561
562 let optimized_plan = match plan {
563 LogicalPlan::Projection(proj) => self.try_optimize_proj(proj, config)?,
564 LogicalPlan::Sort(sort) => self.try_optimize_sort(sort, config)?,
565 LogicalPlan::Filter(filter) => self.try_optimize_filter(filter, config)?,
566 LogicalPlan::Window(window) => self.try_optimize_window(window, config)?,
567 LogicalPlan::Aggregate(agg) => self.try_optimize_aggregate(agg, config)?,
568 LogicalPlan::Join(_)
569 | LogicalPlan::Repartition(_)
570 | LogicalPlan::Union(_)
571 | LogicalPlan::TableScan(_)
572 | LogicalPlan::Values(_)
573 | LogicalPlan::EmptyRelation(_)
574 | LogicalPlan::Subquery(_)
575 | LogicalPlan::SubqueryAlias(_)
576 | LogicalPlan::Limit(_)
577 | LogicalPlan::Ddl(_)
578 | LogicalPlan::Explain(_)
579 | LogicalPlan::Analyze(_)
580 | LogicalPlan::Statement(_)
581 | LogicalPlan::DescribeTable(_)
582 | LogicalPlan::Distinct(_)
583 | LogicalPlan::Extension(_)
584 | LogicalPlan::Dml(_)
585 | LogicalPlan::Copy(_)
586 | LogicalPlan::Unnest(_)
587 | LogicalPlan::RecursiveQuery(_) => {
588 plan.map_uncorrelated_subqueries(|c| self.rewrite(c, config))?
592 .transform_sibling(|plan| {
593 plan.map_children(|c| self.rewrite(c, config))
594 })?
595 }
596 };
597
598 if optimized_plan.transformed && optimized_plan.data.schema() != &original_schema
600 {
601 optimized_plan.map_data(|optimized_plan| {
602 build_recover_project_plan(&original_schema, optimized_plan)
603 })
604 } else {
605 Ok(optimized_plan)
606 }
607 }
608
609 fn name(&self) -> &str {
610 "common_sub_expression_eliminate"
611 }
612}
613
614#[derive(Debug, Clone, Copy)]
616enum ExprMask {
617 Normal,
626
627 NormalAndAggregates,
629}
630
631struct ExprCSEController<'a> {
632 alias_generator: &'a AliasGenerator,
633 mask: ExprMask,
634
635 alias_counter: usize,
637}
638
639impl<'a> ExprCSEController<'a> {
640 fn new(alias_generator: &'a AliasGenerator, mask: ExprMask) -> Self {
641 Self {
642 alias_generator,
643 mask,
644 alias_counter: 0,
645 }
646 }
647}
648
649impl CSEController for ExprCSEController<'_> {
650 type Node = Expr;
651
652 fn conditional_children(node: &Expr) -> Option<(Vec<&Expr>, Vec<&Expr>)> {
653 match node {
654 Expr::ScalarFunction(ScalarFunction { func, args }) => {
658 func.conditional_arguments(args)
659 }
660 Expr::HigherOrderFunction(HigherOrderFunction { func, args }) => {
661 func.conditional_arguments(args)
662 }
663
664 Expr::BinaryExpr(BinaryExpr {
667 left,
668 op: Operator::And | Operator::Or,
669 right,
670 }) => Some((vec![left.as_ref()], vec![right.as_ref()])),
671
672 Expr::Case(Case {
676 expr,
677 when_then_expr,
678 else_expr,
679 }) => Some((
680 expr.iter()
681 .map(|e| e.as_ref())
682 .chain(when_then_expr.iter().take(1).map(|(when, _)| when.as_ref()))
683 .collect(),
684 when_then_expr
685 .iter()
686 .take(1)
687 .map(|(_, then)| then.as_ref())
688 .chain(
689 when_then_expr
690 .iter()
691 .skip(1)
692 .flat_map(|(when, then)| [when.as_ref(), then.as_ref()]),
693 )
694 .chain(else_expr.iter().map(|e| e.as_ref()))
695 .collect(),
696 )),
697 _ => None,
698 }
699 }
700
701 fn is_valid(node: &Expr) -> bool {
702 !node.is_volatile_node()
703 && !matches!(node, Expr::Lambda(_) | Expr::LambdaVariable(_))
704 }
705
706 fn is_ignored(&self, node: &Expr) -> bool {
707 if node.placement() == ExpressionPlacement::MoveTowardsLeafNodes {
716 return true;
717 }
718
719 #[expect(deprecated)]
721 let is_normal_minus_aggregates = matches!(
722 node,
723 Expr::Literal(..)
729 | Expr::Column(..)
730 | Expr::ScalarVariable(..)
731 | Expr::Alias(..)
732 | Expr::Wildcard { .. }
733 | Expr::Lambda(_)
734 | Expr::LambdaVariable(_)
735 );
736
737 let is_aggr = matches!(node, Expr::AggregateFunction(..));
738
739 match self.mask {
740 ExprMask::Normal => is_normal_minus_aggregates || is_aggr,
741 ExprMask::NormalAndAggregates => is_normal_minus_aggregates,
742 }
743 }
744
745 fn generate_alias(&self) -> String {
746 self.alias_generator.next(CSE_PREFIX)
747 }
748
749 fn rewrite(&mut self, node: &Self::Node, alias: &str) -> Self::Node {
750 if self.alias_counter > 0 {
752 col(alias)
753 } else {
754 self.alias_counter += 1;
755 col(alias).alias(node.schema_name().to_string())
756 }
757 }
758
759 fn rewrite_f_down(&mut self, node: &Expr) {
760 if matches!(node, Expr::Alias(_)) {
761 self.alias_counter += 1;
762 }
763 }
764 fn rewrite_f_up(&mut self, node: &Expr) {
765 if matches!(node, Expr::Alias(_)) {
766 self.alias_counter -= 1
767 }
768 }
769}
770
771impl Default for CommonSubexprEliminate {
772 fn default() -> Self {
773 Self::new()
774 }
775}
776
777fn build_common_expr_project_plan(
788 input: LogicalPlan,
789 common_exprs: Vec<(Expr, String)>,
790) -> Result<LogicalPlan> {
791 let mut fields_set = BTreeSet::new();
792 let mut project_exprs = common_exprs
793 .into_iter()
794 .map(|(expr, expr_alias)| {
795 fields_set.insert(expr_alias.clone());
796 Ok(expr.alias(expr_alias))
797 })
798 .collect::<Result<Vec<_>>>()?;
799
800 for (qualifier, field) in input.schema().iter() {
801 if fields_set.insert(qualified_name(qualifier, field.name())) {
802 project_exprs.push(Expr::from((qualifier, field)));
803 }
804 }
805
806 Projection::try_new(project_exprs, Arc::new(input)).map(LogicalPlan::Projection)
807}
808
809fn build_recover_project_plan(
815 schema: &DFSchema,
816 input: LogicalPlan,
817) -> Result<LogicalPlan> {
818 let col_exprs = schema.iter().map(Expr::from).collect();
819 Projection::try_new(col_exprs, Arc::new(input)).map(LogicalPlan::Projection)
820}
821
822fn extract_expressions(expr: &Expr, result: &mut Vec<Expr>) {
823 if let Expr::GroupingSet(groupings) = expr {
824 for e in groupings.distinct_expr() {
825 let (qualifier, field_name) = e.qualified_name();
826 let col = Column::new(qualifier, field_name);
827 result.push(Expr::Column(col))
828 }
829 result.push(Expr::Column(Column::from_name(
830 Aggregate::INTERNAL_GROUPING_ID,
831 )));
832 } else {
833 let (qualifier, field_name) = expr.qualified_name();
834 let col = Column::new(qualifier, field_name);
835 result.push(Expr::Column(col));
836 }
837}
838
839#[cfg(test)]
840mod test {
841
842 use std::iter;
843
844 use arrow::datatypes::{DataType, Field, Schema};
845 use datafusion_expr::logical_plan::{JoinType, table_scan};
846 use datafusion_expr::{
847 AccumulatorFactoryFunction, AggregateUDF, ColumnarValue, ScalarFunctionArgs,
848 ScalarUDF, ScalarUDFImpl, Signature, SimpleAggregateUDF, Volatility,
849 grouping_set, is_null, not,
850 };
851 use datafusion_expr::{lit, logical_plan::builder::LogicalPlanBuilder};
852
853 use super::*;
854 use crate::assert_optimized_plan_eq_snapshot;
855 use crate::optimizer::OptimizerContext;
856 use crate::test::udfs::leaf_udf_expr;
857 use crate::test::*;
858 use datafusion_expr::test::function_stub::{avg, sum};
859
860 macro_rules! assert_optimized_plan_equal {
861 (
862 $config:expr,
863 $plan:expr,
864 @ $expected:literal $(,)?
865 ) => {{
866 let rules: Vec<Arc<dyn crate::OptimizerRule + Send + Sync>> = vec![Arc::new(CommonSubexprEliminate::new())];
867 assert_optimized_plan_eq_snapshot!(
868 $config,
869 rules,
870 $plan,
871 @ $expected,
872 )
873 }};
874
875 (
876 $plan:expr,
877 @ $expected:literal $(,)?
878 ) => {{
879 let rules: Vec<Arc<dyn crate::OptimizerRule + Send + Sync>> = vec![Arc::new(CommonSubexprEliminate::new())];
880 let optimizer_ctx = OptimizerContext::new();
881 assert_optimized_plan_eq_snapshot!(
882 optimizer_ctx,
883 rules,
884 $plan,
885 @ $expected,
886 )
887 }};
888 }
889
890 #[test]
891 fn tpch_q1_simplified() -> Result<()> {
892 let table_scan = test_table_scan()?;
901
902 let plan = LogicalPlanBuilder::from(table_scan)
903 .aggregate(
904 iter::empty::<Expr>(),
905 vec![
906 sum(col("a") * (lit(1) - col("b"))),
907 sum((col("a") * (lit(1) - col("b"))) * (lit(1) + col("c"))),
908 ],
909 )?
910 .build()?;
911
912 assert_optimized_plan_equal!(
913 plan,
914 @ r"
915 Aggregate: groupBy=[[]], aggr=[[sum(__common_expr_1 AS test.a * Int32(1) - test.b), sum(__common_expr_1 AS test.a * Int32(1) - test.b * (Int32(1) + test.c))]]
916 Projection: test.a * (Int32(1) - test.b) AS __common_expr_1, test.a, test.b, test.c
917 TableScan: test
918 "
919 )
920 }
921
922 #[test]
923 fn nested_aliases() -> Result<()> {
924 let table_scan = test_table_scan()?;
925
926 let plan = LogicalPlanBuilder::from(table_scan)
927 .project(vec![
928 (col("a") + col("b") - col("c")).alias("alias1") * (col("a") + col("b")),
929 col("a") + col("b"),
930 ])?
931 .build()?;
932
933 assert_optimized_plan_equal!(
934 plan,
935 @ r"
936 Projection: __common_expr_1 - test.c AS alias1 * __common_expr_1 AS test.a + test.b, __common_expr_1 AS test.a + test.b
937 Projection: test.a + test.b AS __common_expr_1, test.a, test.b, test.c
938 TableScan: test
939 "
940 )
941 }
942
943 #[test]
944 fn aggregate() -> Result<()> {
945 let table_scan = test_table_scan()?;
946
947 let return_type = DataType::UInt32;
948 let accumulator: AccumulatorFactoryFunction = Arc::new(|_| unimplemented!());
949 let udf_agg = |inner: Expr| {
950 Expr::AggregateFunction(datafusion_expr::expr::AggregateFunction::new_udf(
951 Arc::new(AggregateUDF::from(SimpleAggregateUDF::new_with_signature(
952 "my_agg",
953 Signature::exact(vec![DataType::UInt32], Volatility::Stable),
954 return_type.clone(),
955 Arc::clone(&accumulator),
956 vec![Field::new("value", DataType::UInt32, true).into()],
957 ))),
958 vec![inner],
959 false,
960 None,
961 vec![],
962 None,
963 ))
964 };
965
966 let plan = LogicalPlanBuilder::from(table_scan.clone())
968 .aggregate(
969 iter::empty::<Expr>(),
970 vec![
971 avg(col("a")).alias("col1"),
973 avg(col("a")).alias("col2"),
974 avg(col("b")).alias("col3"),
976 avg(col("c")),
977 udf_agg(col("a")).alias("col4"),
979 udf_agg(col("a")).alias("col5"),
980 udf_agg(col("b")).alias("col6"),
982 udf_agg(col("c")),
983 ],
984 )?
985 .build()?;
986
987 assert_optimized_plan_equal!(
988 plan,
989 @ r"
990 Projection: __common_expr_1 AS col1, __common_expr_1 AS col2, col3, __common_expr_3 AS avg(test.c), __common_expr_2 AS col4, __common_expr_2 AS col5, col6, __common_expr_4 AS my_agg(test.c)
991 Aggregate: groupBy=[[]], aggr=[[avg(test.a) AS __common_expr_1, my_agg(test.a) AS __common_expr_2, avg(test.b) AS col3, avg(test.c) AS __common_expr_3, my_agg(test.b) AS col6, my_agg(test.c) AS __common_expr_4]]
992 TableScan: test
993 "
994 )?;
995
996 let plan = LogicalPlanBuilder::from(table_scan.clone())
998 .aggregate(
999 iter::empty::<Expr>(),
1000 vec![
1001 lit(1) + avg(col("a")),
1002 lit(1) - avg(col("a")),
1003 lit(1) + udf_agg(col("a")),
1004 lit(1) - udf_agg(col("a")),
1005 ],
1006 )?
1007 .build()?;
1008
1009 assert_optimized_plan_equal!(
1010 plan,
1011 @ r"
1012 Projection: Int32(1) + __common_expr_1 AS avg(test.a), Int32(1) - __common_expr_1 AS avg(test.a), Int32(1) + __common_expr_2 AS my_agg(test.a), Int32(1) - __common_expr_2 AS my_agg(test.a)
1013 Aggregate: groupBy=[[]], aggr=[[avg(test.a) AS __common_expr_1, my_agg(test.a) AS __common_expr_2]]
1014 TableScan: test
1015 "
1016 )?;
1017
1018 let plan = LogicalPlanBuilder::from(table_scan.clone())
1020 .aggregate(
1021 iter::empty::<Expr>(),
1022 vec![
1023 avg(lit(1u32) + col("a")).alias("col1"),
1024 udf_agg(lit(1u32) + col("a")).alias("col2"),
1025 ],
1026 )?
1027 .build()?;
1028
1029 assert_optimized_plan_equal!(
1030 plan,
1031 @ r"
1032 Aggregate: groupBy=[[]], aggr=[[avg(__common_expr_1) AS col1, my_agg(__common_expr_1) AS col2]]
1033 Projection: UInt32(1) + test.a AS __common_expr_1, test.a, test.b, test.c
1034 TableScan: test
1035 "
1036 )?;
1037
1038 let plan = LogicalPlanBuilder::from(table_scan.clone())
1040 .aggregate(
1041 vec![lit(1u32) + col("a")],
1042 vec![
1043 avg(lit(1u32) + col("a")).alias("col1"),
1044 udf_agg(lit(1u32) + col("a")).alias("col2"),
1045 ],
1046 )?
1047 .build()?;
1048
1049 assert_optimized_plan_equal!(
1050 plan,
1051 @ r"
1052 Aggregate: groupBy=[[__common_expr_1 AS UInt32(1) + test.a]], aggr=[[avg(__common_expr_1) AS col1, my_agg(__common_expr_1) AS col2]]
1053 Projection: UInt32(1) + test.a AS __common_expr_1, test.a, test.b, test.c
1054 TableScan: test
1055 "
1056 )?;
1057
1058 let plan = LogicalPlanBuilder::from(table_scan)
1060 .aggregate(
1061 vec![lit(1u32) + col("a")],
1062 vec![
1063 (lit(1u32) + avg(lit(1u32) + col("a"))).alias("col1"),
1064 (lit(1u32) - avg(lit(1u32) + col("a"))).alias("col2"),
1065 avg(lit(1u32) + col("a")),
1066 (lit(1u32) + udf_agg(lit(1u32) + col("a"))).alias("col3"),
1067 (lit(1u32) - udf_agg(lit(1u32) + col("a"))).alias("col4"),
1068 udf_agg(lit(1u32) + col("a")),
1069 ],
1070 )?
1071 .build()?;
1072
1073 assert_optimized_plan_equal!(
1074 plan,
1075 @ r"
1076 Projection: UInt32(1) + test.a, UInt32(1) + __common_expr_2 AS col1, UInt32(1) - __common_expr_2 AS col2, __common_expr_4 AS avg(UInt32(1) + test.a), UInt32(1) + __common_expr_3 AS col3, UInt32(1) - __common_expr_3 AS col4, __common_expr_5 AS my_agg(UInt32(1) + test.a)
1077 Aggregate: groupBy=[[__common_expr_1 AS UInt32(1) + test.a]], aggr=[[avg(__common_expr_1) AS __common_expr_2, my_agg(__common_expr_1) AS __common_expr_3, avg(__common_expr_1 AS UInt32(1) + test.a) AS __common_expr_4, my_agg(__common_expr_1 AS UInt32(1) + test.a) AS __common_expr_5]]
1078 Projection: UInt32(1) + test.a AS __common_expr_1, test.a, test.b, test.c
1079 TableScan: test
1080 "
1081 )
1082 }
1083
1084 #[test]
1085 fn aggregate_with_relations_and_dots() -> Result<()> {
1086 let schema = Schema::new(vec![Field::new("col.a", DataType::UInt32, false)]);
1087 let table_scan = table_scan(Some("table.test"), &schema, None)?.build()?;
1088
1089 let col_a = Expr::Column(Column::new(Some("table.test"), "col.a"));
1090
1091 let plan = LogicalPlanBuilder::from(table_scan)
1092 .aggregate(
1093 vec![col_a.clone()],
1094 vec![
1095 (lit(1u32) + avg(lit(1u32) + col_a.clone())),
1096 avg(lit(1u32) + col_a),
1097 ],
1098 )?
1099 .build()?;
1100
1101 assert_optimized_plan_equal!(
1102 plan,
1103 @ r"
1104 Projection: table.test.col.a, UInt32(1) + __common_expr_2 AS avg(UInt32(1) + table.test.col.a), __common_expr_2 AS avg(UInt32(1) + table.test.col.a)
1105 Aggregate: groupBy=[[table.test.col.a]], aggr=[[avg(__common_expr_1 AS UInt32(1) + table.test.col.a) AS __common_expr_2]]
1106 Projection: UInt32(1) + table.test.col.a AS __common_expr_1, table.test.col.a
1107 TableScan: table.test
1108 "
1109 )
1110 }
1111
1112 #[test]
1113 fn common_aggregate_grouping_set_preserves_internal_id() -> Result<()> {
1114 let plan = LogicalPlanBuilder::from(test_table_scan()?)
1115 .aggregate(
1116 vec![grouping_set(vec![vec![col("a")]])],
1117 vec![avg(col("b")).alias("first"), avg(col("b")).alias("second")],
1118 )?
1119 .filter(col(Aggregate::INTERNAL_GROUPING_ID).eq(lit(0_u8)))?
1120 .build()?;
1121
1122 assert_optimized_plan_equal!(
1123 plan,
1124 @ r"
1125 Filter: __grouping_id = UInt8(0)
1126 Projection: test.a, __grouping_id, __common_expr_1 AS first, __common_expr_1 AS second
1127 Aggregate: groupBy=[[GROUPING SETS ((test.a))]], aggr=[[avg(test.b) AS __common_expr_1]]
1128 TableScan: test
1129 "
1130 )
1131 }
1132
1133 #[test]
1134 fn subexpr_in_same_order() -> Result<()> {
1135 let table_scan = test_table_scan()?;
1136
1137 let plan = LogicalPlanBuilder::from(table_scan)
1138 .project(vec![
1139 (lit(1) + col("a")).alias("first"),
1140 (lit(1) + col("a")).alias("second"),
1141 ])?
1142 .build()?;
1143
1144 assert_optimized_plan_equal!(
1145 plan,
1146 @ r"
1147 Projection: __common_expr_1 AS first, __common_expr_1 AS second
1148 Projection: Int32(1) + test.a AS __common_expr_1, test.a, test.b, test.c
1149 TableScan: test
1150 "
1151 )
1152 }
1153
1154 #[test]
1155 fn subexpr_in_different_order() -> Result<()> {
1156 let table_scan = test_table_scan()?;
1157
1158 let plan = LogicalPlanBuilder::from(table_scan)
1159 .project(vec![lit(1) + col("a"), col("a") + lit(1)])?
1160 .build()?;
1161
1162 assert_optimized_plan_equal!(
1163 plan,
1164 @ r"
1165 Projection: __common_expr_1 AS Int32(1) + test.a, __common_expr_1 AS test.a + Int32(1)
1166 Projection: Int32(1) + test.a AS __common_expr_1, test.a, test.b, test.c
1167 TableScan: test
1168 "
1169 )
1170 }
1171
1172 #[test]
1173 fn cross_plans_subexpr() -> Result<()> {
1174 let table_scan = test_table_scan()?;
1175
1176 let plan = LogicalPlanBuilder::from(table_scan)
1177 .project(vec![lit(1) + col("a"), col("a")])?
1178 .project(vec![lit(1) + col("a")])?
1179 .build()?;
1180
1181 assert_optimized_plan_equal!(
1182 plan,
1183 @ r"
1184 Projection: Int32(1) + test.a
1185 Projection: Int32(1) + test.a, test.a
1186 TableScan: test
1187 "
1188 )
1189 }
1190
1191 #[test]
1192 fn redundant_project_fields() {
1193 let table_scan = test_table_scan().unwrap();
1194 let c_plus_a = col("c") + col("a");
1195 let b_plus_a = col("b") + col("a");
1196 let common_exprs_1 = vec![
1197 (c_plus_a, format!("{CSE_PREFIX}_1")),
1198 (b_plus_a, format!("{CSE_PREFIX}_2")),
1199 ];
1200 let c_plus_a_2 = col(format!("{CSE_PREFIX}_1"));
1201 let b_plus_a_2 = col(format!("{CSE_PREFIX}_2"));
1202 let common_exprs_2 = vec![
1203 (c_plus_a_2, format!("{CSE_PREFIX}_3")),
1204 (b_plus_a_2, format!("{CSE_PREFIX}_4")),
1205 ];
1206 let project = build_common_expr_project_plan(table_scan, common_exprs_1).unwrap();
1207 let project_2 = build_common_expr_project_plan(project, common_exprs_2).unwrap();
1208
1209 let mut field_set = BTreeSet::new();
1210 for name in project_2.schema().field_names() {
1211 assert!(field_set.insert(name));
1212 }
1213 }
1214
1215 #[test]
1216 fn redundant_project_fields_join_input() {
1217 let table_scan_1 = test_table_scan_with_name("test1").unwrap();
1218 let table_scan_2 = test_table_scan_with_name("test2").unwrap();
1219 let join = LogicalPlanBuilder::from(table_scan_1)
1220 .join(table_scan_2, JoinType::Inner, (vec!["a"], vec!["a"]), None)
1221 .unwrap()
1222 .build()
1223 .unwrap();
1224 let c_plus_a = col("test1.c") + col("test1.a");
1225 let b_plus_a = col("test1.b") + col("test1.a");
1226 let common_exprs_1 = vec![
1227 (c_plus_a, format!("{CSE_PREFIX}_1")),
1228 (b_plus_a, format!("{CSE_PREFIX}_2")),
1229 ];
1230 let c_plus_a_2 = col(format!("{CSE_PREFIX}_1"));
1231 let b_plus_a_2 = col(format!("{CSE_PREFIX}_2"));
1232 let common_exprs_2 = vec![
1233 (c_plus_a_2, format!("{CSE_PREFIX}_3")),
1234 (b_plus_a_2, format!("{CSE_PREFIX}_4")),
1235 ];
1236 let project = build_common_expr_project_plan(join, common_exprs_1).unwrap();
1237 let project_2 = build_common_expr_project_plan(project, common_exprs_2).unwrap();
1238
1239 let mut field_set = BTreeSet::new();
1240 for name in project_2.schema().field_names() {
1241 assert!(field_set.insert(name));
1242 }
1243 }
1244
1245 #[test]
1246 fn eliminated_subexpr_datatype() {
1247 use datafusion_expr::cast;
1248
1249 let schema = Schema::new(vec![
1250 Field::new("a", DataType::UInt64, false),
1251 Field::new("b", DataType::UInt64, false),
1252 Field::new("c", DataType::UInt64, false),
1253 ]);
1254
1255 let plan = table_scan(Some("table"), &schema, None)
1256 .unwrap()
1257 .filter(
1258 cast(col("a"), DataType::Int64)
1259 .lt(lit(1_i64))
1260 .and(cast(col("a"), DataType::Int64).not_eq(lit(1_i64))),
1261 )
1262 .unwrap()
1263 .build()
1264 .unwrap();
1265 let rule = CommonSubexprEliminate::new();
1266 let optimized_plan = rule.rewrite(plan, &OptimizerContext::new()).unwrap();
1267 assert!(optimized_plan.transformed);
1268 let optimized_plan = optimized_plan.data;
1269
1270 let schema = optimized_plan.schema();
1271 let fields_with_datatypes: Vec<_> = schema
1272 .fields()
1273 .iter()
1274 .map(|field| (field.name(), field.data_type()))
1275 .collect();
1276 let formatted_fields_with_datatype = format!("{fields_with_datatypes:#?}");
1277 let expected = r#"[
1278 (
1279 "a",
1280 UInt64,
1281 ),
1282 (
1283 "b",
1284 UInt64,
1285 ),
1286 (
1287 "c",
1288 UInt64,
1289 ),
1290]"#;
1291 assert_eq!(expected, formatted_fields_with_datatype);
1292 }
1293
1294 #[test]
1295 fn filter_schema_changed() -> Result<()> {
1296 let table_scan = test_table_scan()?;
1297
1298 let plan = LogicalPlanBuilder::from(table_scan)
1299 .filter((lit(1) + col("a") - lit(10)).gt(lit(1) + col("a")))?
1300 .build()?;
1301
1302 assert_optimized_plan_equal!(
1303 plan,
1304 @ r"
1305 Projection: test.a, test.b, test.c
1306 Filter: __common_expr_1 - Int32(10) > __common_expr_1
1307 Projection: Int32(1) + test.a AS __common_expr_1, test.a, test.b, test.c
1308 TableScan: test
1309 "
1310 )
1311 }
1312
1313 #[test]
1314 fn test_extract_expressions_from_grouping_set() -> Result<()> {
1315 let mut result = Vec::with_capacity(4);
1316 let grouping = grouping_set(vec![vec![col("a"), col("b")], vec![col("c")]]);
1317 extract_expressions(&grouping, &mut result);
1318
1319 assert_eq!(
1320 result,
1321 vec![
1322 col("a"),
1323 col("b"),
1324 col("c"),
1325 col(Aggregate::INTERNAL_GROUPING_ID),
1326 ]
1327 );
1328 Ok(())
1329 }
1330
1331 #[test]
1332 fn test_extract_expressions_from_grouping_set_with_identical_expr() -> Result<()> {
1333 let mut result = Vec::with_capacity(3);
1334 let grouping = grouping_set(vec![vec![col("a"), col("b")], vec![col("a")]]);
1335 extract_expressions(&grouping, &mut result);
1336 assert_eq!(
1337 result,
1338 vec![col("a"), col("b"), col(Aggregate::INTERNAL_GROUPING_ID),]
1339 );
1340 Ok(())
1341 }
1342
1343 #[test]
1344 fn test_alias_collision() -> Result<()> {
1345 let table_scan = test_table_scan()?;
1346
1347 let config = OptimizerContext::new();
1348 let common_expr_1 = config.alias_generator().next(CSE_PREFIX);
1349 let plan = LogicalPlanBuilder::from(table_scan.clone())
1350 .project(vec![
1351 (col("a") + col("b")).alias(common_expr_1.clone()),
1352 col("c"),
1353 ])?
1354 .project(vec![
1355 col(common_expr_1.clone()).alias("c1"),
1356 col(common_expr_1).alias("c2"),
1357 (col("c") + lit(2)).alias("c3"),
1358 (col("c") + lit(2)).alias("c4"),
1359 ])?
1360 .build()?;
1361
1362 assert_optimized_plan_equal!(
1363 config,
1364 plan,
1365 @ r"
1366 Projection: __common_expr_1 AS c1, __common_expr_1 AS c2, __common_expr_2 AS c3, __common_expr_2 AS c4
1367 Projection: test.c + Int32(2) AS __common_expr_2, __common_expr_1, test.c
1368 Projection: test.a + test.b AS __common_expr_1, test.c
1369 TableScan: test
1370 "
1371 )?;
1372
1373 let config = OptimizerContext::new();
1374 let _common_expr_1 = config.alias_generator().next(CSE_PREFIX);
1375 let common_expr_2 = config.alias_generator().next(CSE_PREFIX);
1376 let plan = LogicalPlanBuilder::from(table_scan)
1377 .project(vec![
1378 (col("a") + col("b")).alias(common_expr_2.clone()),
1379 col("c"),
1380 ])?
1381 .project(vec![
1382 col(common_expr_2.clone()).alias("c1"),
1383 col(common_expr_2).alias("c2"),
1384 (col("c") + lit(2)).alias("c3"),
1385 (col("c") + lit(2)).alias("c4"),
1386 ])?
1387 .build()?;
1388
1389 assert_optimized_plan_equal!(
1390 config,
1391 plan,
1392 @ r"
1393 Projection: __common_expr_2 AS c1, __common_expr_2 AS c2, __common_expr_3 AS c3, __common_expr_3 AS c4
1394 Projection: test.c + Int32(2) AS __common_expr_3, __common_expr_2, test.c
1395 Projection: test.a + test.b AS __common_expr_2, test.c
1396 TableScan: test
1397 "
1398 )?;
1399
1400 Ok(())
1401 }
1402
1403 #[test]
1404 fn test_extract_expressions_from_col() -> Result<()> {
1405 let mut result = Vec::with_capacity(1);
1406 extract_expressions(&col("a"), &mut result);
1407 assert!(result.len() == 1);
1408 Ok(())
1409 }
1410
1411 #[test]
1412 fn test_short_circuits() -> Result<()> {
1413 let table_scan = test_table_scan()?;
1414
1415 let extracted_short_circuit = col("a").eq(lit(0)).or(col("b").eq(lit(0)));
1416 let extracted_short_circuit_leg_1 = (col("a") + col("b")).eq(lit(0));
1417 let not_extracted_short_circuit_leg_2 = (col("a") - col("b")).eq(lit(0));
1418 let extracted_short_circuit_leg_3 = (col("a") * col("b")).eq(lit(0));
1419 let plan = LogicalPlanBuilder::from(table_scan)
1420 .project(vec![
1421 extracted_short_circuit.clone().alias("c1"),
1422 extracted_short_circuit.alias("c2"),
1423 extracted_short_circuit_leg_1
1424 .clone()
1425 .or(not_extracted_short_circuit_leg_2.clone())
1426 .alias("c3"),
1427 extracted_short_circuit_leg_1
1428 .and(not_extracted_short_circuit_leg_2)
1429 .alias("c4"),
1430 extracted_short_circuit_leg_3
1431 .clone()
1432 .or(extracted_short_circuit_leg_3)
1433 .alias("c5"),
1434 ])?
1435 .build()?;
1436
1437 assert_optimized_plan_equal!(
1438 plan,
1439 @ r"
1440 Projection: __common_expr_1 AS c1, __common_expr_1 AS c2, __common_expr_2 OR test.a - test.b = Int32(0) AS c3, __common_expr_2 AND test.a - test.b = Int32(0) AS c4, __common_expr_3 OR __common_expr_3 AS c5
1441 Projection: test.a = Int32(0) OR test.b = Int32(0) AS __common_expr_1, test.a + test.b = Int32(0) AS __common_expr_2, test.a * test.b = Int32(0) AS __common_expr_3, test.a, test.b, test.c
1442 TableScan: test
1443 "
1444 )
1445 }
1446
1447 #[test]
1448 fn test_volatile() -> Result<()> {
1449 let table_scan = test_table_scan()?;
1450
1451 let extracted_child = col("a") + col("b");
1452 let rand = rand_func().call(vec![]);
1453 let not_extracted_volatile = extracted_child + rand;
1454 let plan = LogicalPlanBuilder::from(table_scan)
1455 .project(vec![
1456 not_extracted_volatile.clone().alias("c1"),
1457 not_extracted_volatile.alias("c2"),
1458 ])?
1459 .build()?;
1460
1461 assert_optimized_plan_equal!(
1462 plan,
1463 @ r"
1464 Projection: __common_expr_1 + random() AS c1, __common_expr_1 + random() AS c2
1465 Projection: test.a + test.b AS __common_expr_1, test.a, test.b, test.c
1466 TableScan: test
1467 "
1468 )
1469 }
1470
1471 #[test]
1472 fn test_volatile_short_circuits() -> Result<()> {
1473 let table_scan = test_table_scan()?;
1474
1475 let rand = rand_func().call(vec![]);
1476 let extracted_short_circuit_leg_1 = col("a").eq(lit(0));
1477 let not_extracted_volatile_short_circuit_1 =
1478 extracted_short_circuit_leg_1.or(rand.clone().eq(lit(0)));
1479 let not_extracted_short_circuit_leg_2 = col("b").eq(lit(0));
1480 let not_extracted_volatile_short_circuit_2 =
1481 rand.eq(lit(0)).or(not_extracted_short_circuit_leg_2);
1482 let plan = LogicalPlanBuilder::from(table_scan)
1483 .project(vec![
1484 not_extracted_volatile_short_circuit_1.clone().alias("c1"),
1485 not_extracted_volatile_short_circuit_1.alias("c2"),
1486 not_extracted_volatile_short_circuit_2.clone().alias("c3"),
1487 not_extracted_volatile_short_circuit_2.alias("c4"),
1488 ])?
1489 .build()?;
1490
1491 assert_optimized_plan_equal!(
1492 plan,
1493 @ r"
1494 Projection: __common_expr_1 OR random() = Int32(0) AS c1, __common_expr_1 OR random() = Int32(0) AS c2, random() = Int32(0) OR test.b = Int32(0) AS c3, random() = Int32(0) OR test.b = Int32(0) AS c4
1495 Projection: test.a = Int32(0) AS __common_expr_1, test.a, test.b, test.c
1496 TableScan: test
1497 "
1498 )
1499 }
1500
1501 #[test]
1502 fn test_non_top_level_common_expression() -> Result<()> {
1503 let table_scan = test_table_scan()?;
1504
1505 let common_expr = col("a") + col("b");
1506 let plan = LogicalPlanBuilder::from(table_scan)
1507 .project(vec![
1508 common_expr.clone().alias("c1"),
1509 common_expr.alias("c2"),
1510 ])?
1511 .project(vec![col("c1"), col("c2")])?
1512 .build()?;
1513
1514 assert_optimized_plan_equal!(
1515 plan,
1516 @ r"
1517 Projection: c1, c2
1518 Projection: __common_expr_1 AS c1, __common_expr_1 AS c2
1519 Projection: test.a + test.b AS __common_expr_1, test.a, test.b, test.c
1520 TableScan: test
1521 "
1522 )
1523 }
1524
1525 #[test]
1526 fn test_nested_common_expression() -> Result<()> {
1527 let table_scan = test_table_scan()?;
1528
1529 let nested_common_expr = col("a") + col("b");
1530 let common_expr = nested_common_expr.clone() * nested_common_expr;
1531 let plan = LogicalPlanBuilder::from(table_scan)
1532 .project(vec![
1533 common_expr.clone().alias("c1"),
1534 common_expr.alias("c2"),
1535 ])?
1536 .build()?;
1537
1538 assert_optimized_plan_equal!(
1539 plan,
1540 @ r"
1541 Projection: __common_expr_1 AS c1, __common_expr_1 AS c2
1542 Projection: __common_expr_2 * __common_expr_2 AS __common_expr_1, test.a, test.b, test.c
1543 Projection: test.a + test.b AS __common_expr_2, test.a, test.b, test.c
1544 TableScan: test
1545 "
1546 )
1547 }
1548
1549 #[test]
1550 fn test_normalize_add_expression() -> Result<()> {
1551 let table_scan = test_table_scan()?;
1553 let expr = ((col("a") + col("b")) * (col("b") + col("a"))).eq(lit(30));
1554 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1555
1556 assert_optimized_plan_equal!(
1557 plan,
1558 @ r"
1559 Projection: test.a, test.b, test.c
1560 Filter: __common_expr_1 * __common_expr_1 = Int32(30)
1561 Projection: test.a + test.b AS __common_expr_1, test.a, test.b, test.c
1562 TableScan: test
1563 "
1564 )
1565 }
1566
1567 #[test]
1568 fn test_normalize_multi_expression() -> Result<()> {
1569 let table_scan = test_table_scan()?;
1571 let expr = ((col("a") * col("b")) + (col("b") * col("a"))).eq(lit(30));
1572 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1573
1574 assert_optimized_plan_equal!(
1575 plan,
1576 @ r"
1577 Projection: test.a, test.b, test.c
1578 Filter: __common_expr_1 + __common_expr_1 = Int32(30)
1579 Projection: test.a * test.b AS __common_expr_1, test.a, test.b, test.c
1580 TableScan: test
1581 "
1582 )
1583 }
1584
1585 #[test]
1586 fn test_normalize_bitset_and_expression() -> Result<()> {
1587 let table_scan = test_table_scan()?;
1589 let expr = ((col("a") & col("b")) + (col("b") & col("a"))).eq(lit(30));
1590 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1591
1592 assert_optimized_plan_equal!(
1593 plan,
1594 @ r"
1595 Projection: test.a, test.b, test.c
1596 Filter: __common_expr_1 + __common_expr_1 = Int32(30)
1597 Projection: test.a & test.b AS __common_expr_1, test.a, test.b, test.c
1598 TableScan: test
1599 "
1600 )
1601 }
1602
1603 #[test]
1604 fn test_normalize_bitset_or_expression() -> Result<()> {
1605 let table_scan = test_table_scan()?;
1607 let expr = ((col("a") | col("b")) + (col("b") | col("a"))).eq(lit(30));
1608 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1609
1610 assert_optimized_plan_equal!(
1611 plan,
1612 @ r"
1613 Projection: test.a, test.b, test.c
1614 Filter: __common_expr_1 + __common_expr_1 = Int32(30)
1615 Projection: test.a | test.b AS __common_expr_1, test.a, test.b, test.c
1616 TableScan: test
1617 "
1618 )
1619 }
1620
1621 #[test]
1622 fn test_normalize_bitset_xor_expression() -> Result<()> {
1623 let table_scan = test_table_scan()?;
1625 let expr = ((col("a") ^ col("b")) + (col("b") ^ col("a"))).eq(lit(30));
1626 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1627
1628 assert_optimized_plan_equal!(
1629 plan,
1630 @ r"
1631 Projection: test.a, test.b, test.c
1632 Filter: __common_expr_1 + __common_expr_1 = Int32(30)
1633 Projection: test.a BIT_XOR test.b AS __common_expr_1, test.a, test.b, test.c
1634 TableScan: test
1635 "
1636 )
1637 }
1638
1639 #[test]
1640 fn test_normalize_eq_expression() -> Result<()> {
1641 let table_scan = test_table_scan()?;
1643 let expr = (col("a").eq(col("b"))).and(col("b").eq(col("a")));
1644 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1645
1646 assert_optimized_plan_equal!(
1647 plan,
1648 @ r"
1649 Projection: test.a, test.b, test.c
1650 Filter: __common_expr_1 AND __common_expr_1
1651 Projection: test.a = test.b AS __common_expr_1, test.a, test.b, test.c
1652 TableScan: test
1653 "
1654 )
1655 }
1656
1657 #[test]
1658 fn test_normalize_ne_expression() -> Result<()> {
1659 let table_scan = test_table_scan()?;
1661 let expr = (col("a").not_eq(col("b"))).and(col("b").not_eq(col("a")));
1662 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1663
1664 assert_optimized_plan_equal!(
1665 plan,
1666 @ r"
1667 Projection: test.a, test.b, test.c
1668 Filter: __common_expr_1 AND __common_expr_1
1669 Projection: test.a != test.b AS __common_expr_1, test.a, test.b, test.c
1670 TableScan: test
1671 "
1672 )
1673 }
1674
1675 #[test]
1676 fn test_normalize_complex_expression() -> Result<()> {
1677 let table_scan = test_table_scan()?;
1679 let expr = ((col("a") + col("b") * col("c")) - (col("b") * col("c") + col("a")))
1680 .eq(lit(30));
1681 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1682
1683 assert_optimized_plan_equal!(
1684 plan,
1685 @ r"
1686 Projection: test.a, test.b, test.c
1687 Filter: __common_expr_1 - __common_expr_1 = Int32(30)
1688 Projection: test.a + test.b * test.c AS __common_expr_1, test.a, test.b, test.c
1689 TableScan: test
1690 "
1691 )?;
1692
1693 let table_scan = test_table_scan()?;
1695 let expr = (((col("a") + col("b") / col("c")) * col("c"))
1696 / (col("c") * (col("b") / col("c") + col("a")))
1697 + col("a"))
1698 .eq(lit(30));
1699 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1700
1701 assert_optimized_plan_equal!(
1702 plan,
1703 @ r"
1704 Projection: test.a, test.b, test.c
1705 Filter: __common_expr_1 / __common_expr_1 + test.a = Int32(30)
1706 Projection: (test.a + test.b / test.c) * test.c AS __common_expr_1, test.a, test.b, test.c
1707 TableScan: test
1708 "
1709 )?;
1710
1711 let table_scan = test_table_scan()?;
1713 let expr = ((col("b") / (col("a") + col("c")))
1714 * (col("b") / (col("c") + col("a"))))
1715 .eq(lit(30));
1716 let plan = LogicalPlanBuilder::from(table_scan).filter(expr)?.build()?;
1717 assert_optimized_plan_equal!(
1718 plan,
1719 @ r"
1720 Projection: test.a, test.b, test.c
1721 Filter: __common_expr_1 * __common_expr_1 = Int32(30)
1722 Projection: test.b / (test.a + test.c) AS __common_expr_1, test.a, test.b, test.c
1723 TableScan: test
1724 "
1725 )?;
1726
1727 Ok(())
1728 }
1729
1730 #[derive(Debug, PartialEq, Eq, Hash)]
1731 pub struct TestUdf {
1732 signature: Signature,
1733 }
1734
1735 impl TestUdf {
1736 pub fn new() -> Self {
1737 Self {
1738 signature: Signature::numeric(1, Volatility::Immutable),
1739 }
1740 }
1741 }
1742
1743 impl ScalarUDFImpl for TestUdf {
1744 fn name(&self) -> &str {
1745 "my_udf"
1746 }
1747
1748 fn signature(&self) -> &Signature {
1749 &self.signature
1750 }
1751
1752 fn return_type(&self, _: &[DataType]) -> Result<DataType> {
1753 Ok(DataType::Int32)
1754 }
1755
1756 fn invoke_with_args(&self, _args: ScalarFunctionArgs) -> Result<ColumnarValue> {
1757 panic!("not implemented")
1758 }
1759 }
1760
1761 #[test]
1762 fn test_normalize_inner_binary_expression() -> Result<()> {
1763 let table_scan = test_table_scan()?;
1765 let expr1 = not(col("a").eq(col("b")));
1766 let expr2 = not(col("b").eq(col("a")));
1767 let plan = LogicalPlanBuilder::from(table_scan)
1768 .project(vec![expr1, expr2])?
1769 .build()?;
1770 assert_optimized_plan_equal!(
1771 plan,
1772 @ r"
1773 Projection: __common_expr_1 AS NOT test.a = test.b, __common_expr_1 AS NOT test.b = test.a
1774 Projection: NOT test.a = test.b AS __common_expr_1, test.a, test.b, test.c
1775 TableScan: test
1776 "
1777 )?;
1778
1779 let table_scan = test_table_scan()?;
1781 let expr1 = is_null(col("a").eq(col("b")));
1782 let expr2 = is_null(col("b").eq(col("a")));
1783 let plan = LogicalPlanBuilder::from(table_scan)
1784 .project(vec![expr1, expr2])?
1785 .build()?;
1786 assert_optimized_plan_equal!(
1787 plan,
1788 @ r"
1789 Projection: __common_expr_1 AS test.a = test.b IS NULL, __common_expr_1 AS test.b = test.a IS NULL
1790 Projection: test.a = test.b IS NULL AS __common_expr_1, test.a, test.b, test.c
1791 TableScan: test
1792 "
1793 )?;
1794
1795 let table_scan = test_table_scan()?;
1797 let expr1 = (col("a") + col("b")).between(lit(0), lit(10));
1798 let expr2 = (col("b") + col("a")).between(lit(0), lit(10));
1799 let plan = LogicalPlanBuilder::from(table_scan)
1800 .project(vec![expr1, expr2])?
1801 .build()?;
1802 assert_optimized_plan_equal!(
1803 plan,
1804 @ r"
1805 Projection: __common_expr_1 AS test.a + test.b BETWEEN Int32(0) AND Int32(10), __common_expr_1 AS test.b + test.a BETWEEN Int32(0) AND Int32(10)
1806 Projection: test.a + test.b BETWEEN Int32(0) AND Int32(10) AS __common_expr_1, test.a, test.b, test.c
1807 TableScan: test
1808 "
1809 )?;
1810
1811 let table_scan = test_table_scan()?;
1813 let expr1 = col("c").between(col("a") + col("b"), lit(10));
1814 let expr2 = col("c").between(col("b") + col("a"), lit(10));
1815 let plan = LogicalPlanBuilder::from(table_scan)
1816 .project(vec![expr1, expr2])?
1817 .build()?;
1818 assert_optimized_plan_equal!(
1819 plan,
1820 @ r"
1821 Projection: __common_expr_1 AS test.c BETWEEN test.a + test.b AND Int32(10), __common_expr_1 AS test.c BETWEEN test.b + test.a AND Int32(10)
1822 Projection: test.c BETWEEN test.a + test.b AND Int32(10) AS __common_expr_1, test.a, test.b, test.c
1823 TableScan: test
1824 "
1825 )?;
1826
1827 let udf = ScalarUDF::from(TestUdf::new());
1829 let table_scan = test_table_scan()?;
1830 let expr1 = udf.call(vec![col("a") + col("b")]);
1831 let expr2 = udf.call(vec![col("b") + col("a")]);
1832 let plan = LogicalPlanBuilder::from(table_scan)
1833 .project(vec![expr1, expr2])?
1834 .build()?;
1835 assert_optimized_plan_equal!(
1836 plan,
1837 @ r"
1838 Projection: __common_expr_1 AS my_udf(test.a + test.b), __common_expr_1 AS my_udf(test.b + test.a)
1839 Projection: my_udf(test.a + test.b) AS __common_expr_1, test.a, test.b, test.c
1840 TableScan: test
1841 "
1842 )
1843 }
1844
1845 fn rand_func() -> ScalarUDF {
1851 ScalarUDF::new_from_impl(RandomStub::new())
1852 }
1853
1854 #[derive(Debug, PartialEq, Eq, Hash)]
1855 struct RandomStub {
1856 signature: Signature,
1857 }
1858
1859 impl RandomStub {
1860 fn new() -> Self {
1861 Self {
1862 signature: Signature::exact(vec![], Volatility::Volatile),
1863 }
1864 }
1865 }
1866 impl ScalarUDFImpl for RandomStub {
1867 fn name(&self) -> &str {
1868 "random"
1869 }
1870
1871 fn signature(&self) -> &Signature {
1872 &self.signature
1873 }
1874
1875 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
1876 Ok(DataType::Float64)
1877 }
1878
1879 fn invoke_with_args(&self, _args: ScalarFunctionArgs) -> Result<ColumnarValue> {
1880 panic!("dummy - not implemented")
1881 }
1882 }
1883
1884 #[test]
1891 fn test_leaf_expression_not_extracted() -> Result<()> {
1892 let table_scan = test_table_scan()?;
1893
1894 let leaf = leaf_udf_expr(col("a"));
1895 let plan = LogicalPlanBuilder::from(table_scan)
1896 .project(vec![leaf.clone().alias("c1"), leaf.alias("c2")])?
1897 .build()?;
1898
1899 assert_optimized_plan_equal!(
1901 plan,
1902 @r"
1903 Projection: leaf_udf(test.a) AS c1, leaf_udf(test.a) AS c2
1904 TableScan: test
1905 "
1906 )
1907 }
1908
1909 #[test]
1913 fn test_leaf_subexpression_not_extracted() -> Result<()> {
1914 let table_scan = test_table_scan()?;
1915
1916 let common = leaf_udf_expr(col("a")) + col("b");
1920 let plan = LogicalPlanBuilder::from(table_scan)
1921 .project(vec![common.clone().alias("c1"), common.alias("c2")])?
1922 .build()?;
1923
1924 assert_optimized_plan_equal!(
1927 plan,
1928 @r"
1929 Projection: __common_expr_1 AS c1, __common_expr_1 AS c2
1930 Projection: leaf_udf(test.a) + test.b AS __common_expr_1, test.a, test.b, test.c
1931 TableScan: test
1932 "
1933 )
1934 }
1935}