datafusion_expr/expr_rewriter/
mod.rs1use std::collections::HashMap;
21use std::collections::HashSet;
22use std::fmt::Debug;
23use std::sync::Arc;
24
25use crate::expr::{Alias, Sort, Unnest};
26use crate::logical_plan::Projection;
27use crate::{Expr, ExprSchemable, LogicalPlan, LogicalPlanBuilder};
28
29use datafusion_common::TableReference;
30use datafusion_common::config::ConfigOptions;
31use datafusion_common::tree_node::{Transformed, TransformedResult, TreeNode};
32use datafusion_common::{Column, DFSchema, Result};
33
34mod guarantees;
35pub use guarantees::GuaranteeRewriter;
36pub use guarantees::rewrite_with_guarantees;
37pub use guarantees::rewrite_with_guarantees_map;
38mod order_by;
39
40pub use order_by::rewrite_sort_cols_by_aggs;
41
42pub trait FunctionRewrite: Debug {
52 fn name(&self) -> &str;
54
55 fn rewrite(
60 &self,
61 expr: Expr,
62 schema: &DFSchema,
63 config: &ConfigOptions,
64 ) -> Result<Transformed<Expr>>;
65}
66
67pub fn normalize_col(expr: Expr, plan: &LogicalPlan) -> Result<Expr> {
70 expr.transform(|expr| {
71 Ok({
72 if let Expr::Column(c) = expr {
73 let col = LogicalPlanBuilder::normalize(plan, c)?;
74 Transformed::yes(Expr::Column(col))
75 } else {
76 Transformed::no(expr)
77 }
78 })
79 })
80 .data()
81}
82
83pub fn normalize_col_with_schemas_and_ambiguity_check(
85 expr: Expr,
86 schemas: &[&[&DFSchema]],
87 using_columns: &[HashSet<Column>],
88) -> Result<Expr> {
89 if let Expr::Unnest(Unnest { expr, outer }) = expr {
91 let e = normalize_col_with_schemas_and_ambiguity_check(
92 expr.as_ref().clone(),
93 schemas,
94 using_columns,
95 )?;
96 return Ok(Expr::Unnest(Unnest {
97 expr: Box::new(e),
98 outer,
99 }));
100 }
101
102 expr.transform(|expr| {
103 Ok({
104 if let Expr::Column(c) = expr {
105 let col =
106 c.normalize_with_schemas_and_ambiguity_check(schemas, using_columns)?;
107 Transformed::yes(Expr::Column(col))
108 } else {
109 Transformed::no(expr)
110 }
111 })
112 })
113 .data()
114}
115
116pub fn normalize_cols(
118 exprs: impl IntoIterator<Item = impl Into<Expr>>,
119 plan: &LogicalPlan,
120) -> Result<Vec<Expr>> {
121 exprs
122 .into_iter()
123 .map(|e| normalize_col(e.into(), plan))
124 .collect()
125}
126
127pub fn normalize_sorts(
128 sorts: impl IntoIterator<Item = impl Into<Sort>>,
129 plan: &LogicalPlan,
130) -> Result<Vec<Sort>> {
131 sorts
132 .into_iter()
133 .map(|e| {
134 let sort = e.into();
135 normalize_col(sort.expr, plan)
136 .map(|expr| Sort::new(expr, sort.asc, sort.nulls_first))
137 })
138 .collect()
139}
140
141pub fn replace_col(expr: Expr, replace_map: &HashMap<&Column, &Column>) -> Result<Expr> {
144 expr.transform(|expr| {
145 Ok({
146 if let Expr::Column(c) = &expr {
147 match replace_map.get(c) {
148 Some(new_c) => Transformed::yes(Expr::Column((*new_c).to_owned())),
149 None => Transformed::no(expr),
150 }
151 } else {
152 Transformed::no(expr)
153 }
154 })
155 })
156 .data()
157}
158
159pub fn unnormalize_col(expr: Expr) -> Expr {
165 expr.transform(|expr| {
166 Ok({
167 if let Expr::Column(c) = expr {
168 let col = Column::new_unqualified(c.name);
169 Transformed::yes(Expr::Column(col))
170 } else {
171 Transformed::no(expr)
172 }
173 })
174 })
175 .data()
176 .expect("Unnormalize is infallible")
177}
178
179pub fn create_col_from_scalar_expr(
181 scalar_expr: &Expr,
182 subqry_alias: String,
183) -> Result<Column> {
184 match scalar_expr {
185 Expr::Alias(Alias { name, .. }) => Ok(Column::new(
186 Some::<TableReference>(subqry_alias.into()),
187 name,
188 )),
189 Expr::Column(col) => Ok(col.with_relation(subqry_alias.into())),
190 _ => {
191 let scalar_column = scalar_expr.schema_name().to_string();
192 Ok(Column::new(
193 Some::<TableReference>(subqry_alias.into()),
194 scalar_column,
195 ))
196 }
197 }
198}
199
200#[inline]
202pub fn unnormalize_cols(exprs: impl IntoIterator<Item = Expr>) -> Vec<Expr> {
203 exprs.into_iter().map(unnormalize_col).collect()
204}
205
206pub fn strip_outer_reference(expr: Expr) -> Expr {
209 expr.transform(|expr| {
210 Ok({
211 if let Expr::OuterReferenceColumn(_, col) = expr {
212 Transformed::yes(Expr::Column(col))
213 } else {
214 Transformed::no(expr)
215 }
216 })
217 })
218 .data()
219 .expect("strip_outer_reference is infallible")
220}
221
222pub fn coerce_plan_expr_for_schema(
225 plan: LogicalPlan,
226 schema: &DFSchema,
227) -> Result<LogicalPlan> {
228 match plan {
229 LogicalPlan::Projection(Projection { expr, input, .. }) => {
231 let new_exprs = coerce_exprs_for_schema(expr, input.schema(), schema)?;
232 let projection = Projection::try_new(new_exprs, input)?;
233 Ok(LogicalPlan::Projection(projection))
234 }
235 _ => {
236 let exprs: Vec<Expr> = plan.schema().iter().map(Expr::from).collect();
237 let new_exprs = coerce_exprs_for_schema(exprs, plan.schema(), schema)?;
238 let add_project = new_exprs.iter().any(|expr| expr.try_as_col().is_none());
239 if add_project {
240 let projection = Projection::try_new(new_exprs, Arc::new(plan))?;
241 Ok(LogicalPlan::Projection(projection))
242 } else {
243 Ok(plan)
244 }
245 }
246 }
247}
248
249fn coerce_exprs_for_schema(
250 exprs: Vec<Expr>,
251 src_schema: &DFSchema,
252 dst_schema: &DFSchema,
253) -> Result<Vec<Expr>> {
254 exprs
255 .into_iter()
256 .enumerate()
257 .map(|(idx, expr)| {
258 let new_type = dst_schema.field(idx).data_type();
259 if new_type != &expr.get_type(src_schema)? {
260 match expr {
261 Expr::Alias(Alias { expr, name, .. }) => {
262 Ok(expr.cast_to(new_type, src_schema)?.alias(name))
263 }
264 #[expect(deprecated)]
265 Expr::Wildcard { .. } => Ok(expr),
266 _ => {
267 match expr {
268 Expr::Column(ref column) => {
272 let name = column.name().to_owned();
273 Ok(expr.cast_to(new_type, src_schema)?.alias(name))
274 }
275 _ => Ok(expr.cast_to(new_type, src_schema)?),
276 }
277 }
278 }
279 } else {
280 Ok(expr)
281 }
282 })
283 .collect::<Result<_>>()
284}
285
286#[inline]
288pub fn unalias(expr: Expr) -> Expr {
289 match expr {
290 Expr::Alias(Alias { expr, .. }) => unalias(*expr),
291 _ => expr,
292 }
293}
294
295pub struct NamePreserver {
304 use_alias: bool,
305}
306
307#[derive(Debug)]
310pub enum SavedName {
311 Saved {
313 relation: Option<TableReference>,
314 name: String,
315 },
316 None,
318}
319
320impl NamePreserver {
321 pub fn new(plan: &LogicalPlan) -> Self {
323 Self {
324 use_alias: !matches!(
327 plan,
328 LogicalPlan::Filter(_)
329 | LogicalPlan::Join(_)
330 | LogicalPlan::TableScan(_)
331 | LogicalPlan::Limit(_)
332 | LogicalPlan::Statement(_)
333 ),
334 }
335 }
336
337 pub fn new_for_projection() -> Self {
341 Self { use_alias: true }
342 }
343
344 pub fn save(&self, expr: &Expr) -> SavedName {
345 if self.use_alias {
346 match expr {
347 Expr::Alias(alias) => SavedName::Saved {
348 relation: alias.relation.clone(),
349 name: alias.name.clone(),
350 },
351 _ => {
352 let (relation, name) = expr.qualified_name();
353 SavedName::Saved { relation, name }
354 }
355 }
356 } else {
357 SavedName::None
358 }
359 }
360}
361
362impl SavedName {
363 pub fn restore(self, expr: Expr) -> Expr {
365 match self {
366 SavedName::Saved { relation, name } => {
367 let (new_relation, new_name) = expr.qualified_name();
368 if new_relation != relation || new_name != name {
369 expr.alias_qualified(relation, name)
370 } else {
371 expr
372 }
373 }
374 SavedName::None => expr,
375 }
376 }
377}
378
379#[cfg(test)]
380mod test {
381 use std::ops::Add;
382
383 use super::*;
384 use crate::literal::lit_with_metadata;
385 use crate::{Cast, col, lit};
386 use arrow::datatypes::{DataType, Field, Schema};
387 use datafusion_common::ScalarValue;
388 use datafusion_common::tree_node::TreeNodeRewriter;
389
390 #[derive(Default)]
391 struct RecordingRewriter {
392 v: Vec<String>,
393 }
394
395 impl TreeNodeRewriter for RecordingRewriter {
396 type Node = Expr;
397
398 fn f_down(&mut self, expr: Expr) -> Result<Transformed<Expr>> {
399 self.v.push(format!("Previsited {expr}"));
400 Ok(Transformed::no(expr))
401 }
402
403 fn f_up(&mut self, expr: Expr) -> Result<Transformed<Expr>> {
404 self.v.push(format!("Mutated {expr}"));
405 Ok(Transformed::no(expr))
406 }
407 }
408
409 #[test]
410 fn rewriter_rewrite() {
411 let transformer = |expr: Expr| -> Result<Transformed<Expr>> {
413 match expr {
414 Expr::Literal(ScalarValue::Utf8(Some(utf8_val)), metadata) => {
415 let utf8_val = if utf8_val == "foo" {
416 "bar".to_string()
417 } else {
418 utf8_val
419 };
420 Ok(Transformed::yes(lit_with_metadata(utf8_val, metadata)))
421 }
422 _ => Ok(Transformed::no(expr)),
424 }
425 };
426
427 let rewritten = col("state")
429 .eq(lit("foo"))
430 .transform(transformer)
431 .data()
432 .unwrap();
433 assert_eq!(rewritten, col("state").eq(lit("bar")));
434
435 let rewritten = col("state")
437 .eq(lit("baz"))
438 .transform(transformer)
439 .data()
440 .unwrap();
441 assert_eq!(rewritten, col("state").eq(lit("baz")));
442 }
443
444 #[test]
445 fn normalize_cols() {
446 let expr = col("a") + col("b") + col("c");
447
448 let schema_a = make_schema_with_empty_metadata(
450 vec![Some("tableA".into()), Some("tableA".into())],
451 vec!["a", "aa"],
452 );
453 let schema_c = make_schema_with_empty_metadata(
454 vec![Some("tableC".into()), Some("tableC".into())],
455 vec!["cc", "c"],
456 );
457 let schema_b =
458 make_schema_with_empty_metadata(vec![Some("tableB".into())], vec!["b"]);
459 let schema_f = make_schema_with_empty_metadata(
461 vec![Some("tableC".into()), Some("tableC".into())],
462 vec!["f", "ff"],
463 );
464 let schemas = [schema_c, schema_f, schema_b, schema_a];
465 let schemas = schemas.iter().collect::<Vec<_>>();
466
467 let normalized_expr =
468 normalize_col_with_schemas_and_ambiguity_check(expr, &[&schemas], &[])
469 .unwrap();
470 assert_eq!(
471 normalized_expr,
472 col("tableA.a") + col("tableB.b") + col("tableC.c")
473 );
474 }
475
476 #[test]
477 fn normalize_cols_non_exist() {
478 let expr = col("a") + col("b");
480 let schema_a =
481 make_schema_with_empty_metadata(vec![Some("\"tableA\"".into())], vec!["a"]);
482 let schemas = [schema_a];
483 let schemas = schemas.iter().collect::<Vec<_>>();
484
485 let error =
486 normalize_col_with_schemas_and_ambiguity_check(expr, &[&schemas], &[])
487 .unwrap_err()
488 .strip_backtrace();
489 let expected = "Schema error: No field named b.\n\
490 Valid fields are \"tableA\".a.";
491 assert_eq!(error, expected);
492 }
493
494 #[test]
495 fn unnormalize_cols() {
496 let expr = col("tableA.a") + col("tableB.b");
497 let unnormalized_expr = unnormalize_col(expr);
498 assert_eq!(unnormalized_expr, col("a") + col("b"));
499 }
500
501 fn make_schema_with_empty_metadata(
502 qualifiers: Vec<Option<TableReference>>,
503 fields: Vec<&str>,
504 ) -> DFSchema {
505 let fields = fields
506 .iter()
507 .map(|f| Arc::new(Field::new((*f).to_string(), DataType::Int8, false)))
508 .collect::<Vec<_>>();
509 let schema = Arc::new(Schema::new(fields));
510 DFSchema::from_field_specific_qualified_schema(qualifiers, &schema).unwrap()
511 }
512
513 #[test]
514 fn rewriter_visit() {
515 let mut rewriter = RecordingRewriter::default();
516 col("state").eq(lit("CO")).rewrite(&mut rewriter).unwrap();
517
518 assert_eq!(
519 rewriter.v,
520 vec![
521 "Previsited state = Utf8(\"CO\")",
522 "Previsited state",
523 "Mutated state",
524 "Previsited Utf8(\"CO\")",
525 "Mutated Utf8(\"CO\")",
526 "Mutated state = Utf8(\"CO\")"
527 ]
528 )
529 }
530
531 #[test]
532 fn test_rewrite_preserving_name() {
533 test_rewrite(col("a"), col("a"));
534
535 test_rewrite(col("a"), col("b"));
536
537 test_rewrite(
539 col("a"),
540 Expr::Cast(Cast::new(Box::new(col("a")), DataType::Int32)),
541 );
542
543 test_rewrite(col("a").add(lit(1i32)), col("a").add(lit(1i64)));
545
546 test_rewrite(
548 Expr::Column(Column::new(Some("test"), "a")),
549 Expr::Column(Column::new_unqualified("test.a")),
550 );
551 test_rewrite(
552 Expr::Column(Column::new_unqualified("test.a")),
553 Expr::Column(Column::new(Some("test"), "a")),
554 );
555 }
556
557 fn test_rewrite(expr_from: Expr, rewrite_to: Expr) {
560 struct TestRewriter {
561 rewrite_to: Expr,
562 }
563
564 impl TreeNodeRewriter for TestRewriter {
565 type Node = Expr;
566
567 fn f_up(&mut self, _: Expr) -> Result<Transformed<Expr>> {
568 Ok(Transformed::yes(self.rewrite_to.clone()))
569 }
570 }
571
572 let mut rewriter = TestRewriter {
573 rewrite_to: rewrite_to.clone(),
574 };
575 let saved_name = NamePreserver { use_alias: true }.save(&expr_from);
576 let new_expr = expr_from.clone().rewrite(&mut rewriter).unwrap().data;
577 let new_expr = saved_name.restore(new_expr);
578
579 let original_name = expr_from.qualified_name();
580 let new_name = new_expr.qualified_name();
581 assert_eq!(
582 original_name, new_name,
583 "mismatch rewriting expr_from: {expr_from} to {rewrite_to}"
584 )
585 }
586}