datafusion_physical_expr/simplifier/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Simplifier for Physical Expressions
19
20use arrow::datatypes::Schema;
21use datafusion_common::{Result, tree_node::TreeNode};
22use std::sync::Arc;
23
24use crate::{PhysicalExpr, simplifier::not::simplify_not_expr};
25
26pub mod const_evaluator;
27pub mod not;
28pub mod unwrap_cast;
29
30const MAX_LOOP_COUNT: usize = 5;
31
32/// Simplifies physical expressions by applying various optimizations
33///
34/// This can be useful after adapting expressions from a table schema
35/// to a file schema. For example, casts added to match the types may
36/// potentially be unwrapped.
37pub struct PhysicalExprSimplifier<'a> {
38    schema: &'a Schema,
39}
40
41impl<'a> PhysicalExprSimplifier<'a> {
42    /// Create a new physical expression simplifier
43    pub fn new(schema: &'a Schema) -> Self {
44        Self { schema }
45    }
46
47    /// Simplify a physical expression
48    pub fn simplify(&self, expr: Arc<dyn PhysicalExpr>) -> Result<Arc<dyn PhysicalExpr>> {
49        let mut current_expr = expr;
50        let mut count = 0;
51        let schema = self.schema;
52
53        while count < MAX_LOOP_COUNT {
54            count += 1;
55            let result = current_expr.transform(|node| {
56                #[cfg(test)]
57                let original_type = node.data_type(schema).unwrap();
58
59                // Apply NOT expression simplification first, then unwrap cast optimization,
60                // then constant expression evaluation
61                let rewritten = simplify_not_expr(&node, schema)?
62                    .transform_data(|node| {
63                        unwrap_cast::unwrap_cast_in_comparison(node, schema)
64                    })?
65                    .transform_data(|node| const_evaluator::simplify_const_expr(&node))?;
66
67                #[cfg(test)]
68                assert_eq!(
69                    rewritten.data.data_type(schema).unwrap(),
70                    original_type,
71                    "Simplified expression should have the same data type as the original"
72                );
73
74                Ok(rewritten)
75            })?;
76
77            if !result.transformed {
78                return Ok(result.data);
79            }
80            current_expr = result.data;
81        }
82        Ok(current_expr)
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::expressions::{
90        BinaryExpr, CastExpr, Literal, NotExpr, TryCastExpr, col, in_list, lit,
91    };
92    use arrow::datatypes::{DataType, Field, Schema};
93    use datafusion_common::ScalarValue;
94    use datafusion_expr::Operator;
95
96    fn test_schema() -> Schema {
97        Schema::new(vec![
98            Field::new("c1", DataType::Int32, false),
99            Field::new("c2", DataType::Int64, false),
100            Field::new("c3", DataType::Utf8, false),
101        ])
102    }
103
104    fn not_test_schema() -> Schema {
105        Schema::new(vec![
106            Field::new("a", DataType::Boolean, false),
107            Field::new("b", DataType::Boolean, false),
108            Field::new("c", DataType::Int32, false),
109        ])
110    }
111
112    /// Helper function to extract a Literal from a PhysicalExpr
113    fn as_literal(expr: &Arc<dyn PhysicalExpr>) -> &Literal {
114        expr.as_any()
115            .downcast_ref::<Literal>()
116            .unwrap_or_else(|| panic!("Expected Literal, got: {expr}"))
117    }
118
119    /// Helper function to extract a BinaryExpr from a PhysicalExpr
120    fn as_binary(expr: &Arc<dyn PhysicalExpr>) -> &BinaryExpr {
121        expr.as_any()
122            .downcast_ref::<BinaryExpr>()
123            .unwrap_or_else(|| panic!("Expected BinaryExpr, got: {expr}"))
124    }
125
126    /// Assert that simplifying `input` produces `expected`
127    fn assert_not_simplify(
128        simplifier: &PhysicalExprSimplifier,
129        input: Arc<dyn PhysicalExpr>,
130        expected: Arc<dyn PhysicalExpr>,
131    ) {
132        let result = simplifier.simplify(Arc::clone(&input)).unwrap();
133        assert_eq!(
134            &result, &expected,
135            "Simplification should transform:\n  input: {input}\n  to:    {expected}\n  got:   {result}"
136        );
137    }
138
139    #[test]
140    fn test_simplify() {
141        let schema = test_schema();
142        let simplifier = PhysicalExprSimplifier::new(&schema);
143
144        // Create: cast(c2 as INT32) != INT32(99)
145        let column_expr = col("c2", &schema).unwrap();
146        let cast_expr = Arc::new(CastExpr::new(column_expr, DataType::Int32, None));
147        let literal_expr = lit(ScalarValue::Int32(Some(99)));
148        let binary_expr =
149            Arc::new(BinaryExpr::new(cast_expr, Operator::NotEq, literal_expr));
150
151        // Apply full simplification (uses TreeNodeRewriter)
152        let optimized = simplifier.simplify(binary_expr).unwrap();
153
154        let optimized_binary = as_binary(&optimized);
155
156        // Should be optimized to: c2 != INT64(99) (c2 is INT64, literal cast to match)
157        let left_expr = optimized_binary.left();
158        assert!(
159            left_expr.as_any().downcast_ref::<CastExpr>().is_none()
160                && left_expr.as_any().downcast_ref::<TryCastExpr>().is_none()
161        );
162        let right_literal = as_literal(optimized_binary.right());
163        assert_eq!(right_literal.value(), &ScalarValue::Int64(Some(99)));
164    }
165
166    #[test]
167    fn test_nested_expression_simplification() {
168        let schema = test_schema();
169        let simplifier = PhysicalExprSimplifier::new(&schema);
170
171        // Create nested expression: (cast(c1 as INT64) > INT64(5)) OR (cast(c2 as INT32) <= INT32(10))
172        let c1_expr = col("c1", &schema).unwrap();
173        let c1_cast = Arc::new(CastExpr::new(c1_expr, DataType::Int64, None));
174        let c1_literal = lit(ScalarValue::Int64(Some(5)));
175        let c1_binary = Arc::new(BinaryExpr::new(c1_cast, Operator::Gt, c1_literal));
176
177        let c2_expr = col("c2", &schema).unwrap();
178        let c2_cast = Arc::new(CastExpr::new(c2_expr, DataType::Int32, None));
179        let c2_literal = lit(ScalarValue::Int32(Some(10)));
180        let c2_binary = Arc::new(BinaryExpr::new(c2_cast, Operator::LtEq, c2_literal));
181
182        let or_expr = Arc::new(BinaryExpr::new(c1_binary, Operator::Or, c2_binary));
183
184        // Apply simplification
185        let optimized = simplifier.simplify(or_expr).unwrap();
186
187        let or_binary = as_binary(&optimized);
188
189        // Verify left side: c1 > INT32(5)
190        let left_binary = as_binary(or_binary.left());
191        let left_left_expr = left_binary.left();
192        assert!(
193            left_left_expr.as_any().downcast_ref::<CastExpr>().is_none()
194                && left_left_expr
195                    .as_any()
196                    .downcast_ref::<TryCastExpr>()
197                    .is_none()
198        );
199        let left_literal = as_literal(left_binary.right());
200        assert_eq!(left_literal.value(), &ScalarValue::Int32(Some(5)));
201
202        // Verify right side: c2 <= INT64(10)
203        let right_binary = as_binary(or_binary.right());
204        let right_left_expr = right_binary.left();
205        assert!(
206            right_left_expr
207                .as_any()
208                .downcast_ref::<CastExpr>()
209                .is_none()
210                && right_left_expr
211                    .as_any()
212                    .downcast_ref::<TryCastExpr>()
213                    .is_none()
214        );
215        let right_literal = as_literal(right_binary.right());
216        assert_eq!(right_literal.value(), &ScalarValue::Int64(Some(10)));
217    }
218
219    #[test]
220    fn test_double_negation_elimination() -> Result<()> {
221        let schema = not_test_schema();
222        let simplifier = PhysicalExprSimplifier::new(&schema);
223
224        // NOT(NOT(c > 5)) -> c > 5
225        let inner_expr: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
226            col("c", &schema)?,
227            Operator::Gt,
228            lit(ScalarValue::Int32(Some(5))),
229        ));
230        let inner_not = Arc::new(NotExpr::new(Arc::clone(&inner_expr)));
231        let double_not: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(inner_not));
232
233        let expected = inner_expr;
234        assert_not_simplify(&simplifier, double_not, expected);
235        Ok(())
236    }
237
238    #[test]
239    fn test_not_literal() -> Result<()> {
240        let schema = not_test_schema();
241        let simplifier = PhysicalExprSimplifier::new(&schema);
242
243        // NOT(TRUE) -> FALSE
244        let not_true = Arc::new(NotExpr::new(lit(ScalarValue::Boolean(Some(true)))));
245        let expected = lit(ScalarValue::Boolean(Some(false)));
246        assert_not_simplify(&simplifier, not_true, expected);
247
248        // NOT(FALSE) -> TRUE
249        let not_false = Arc::new(NotExpr::new(lit(ScalarValue::Boolean(Some(false)))));
250        let expected = lit(ScalarValue::Boolean(Some(true)));
251        assert_not_simplify(&simplifier, not_false, expected);
252
253        Ok(())
254    }
255
256    #[test]
257    fn test_negate_comparison() -> Result<()> {
258        let schema = not_test_schema();
259        let simplifier = PhysicalExprSimplifier::new(&schema);
260
261        // NOT(c = 5) -> c != 5
262        let not_eq = Arc::new(NotExpr::new(Arc::new(BinaryExpr::new(
263            col("c", &schema)?,
264            Operator::Eq,
265            lit(ScalarValue::Int32(Some(5))),
266        ))));
267        let expected = Arc::new(BinaryExpr::new(
268            col("c", &schema)?,
269            Operator::NotEq,
270            lit(ScalarValue::Int32(Some(5))),
271        ));
272        assert_not_simplify(&simplifier, not_eq, expected);
273
274        Ok(())
275    }
276
277    #[test]
278    fn test_demorgans_law_and() -> Result<()> {
279        let schema = not_test_schema();
280        let simplifier = PhysicalExprSimplifier::new(&schema);
281
282        // NOT(a AND b) -> NOT a OR NOT b
283        let and_expr = Arc::new(BinaryExpr::new(
284            col("a", &schema)?,
285            Operator::And,
286            col("b", &schema)?,
287        ));
288        let not_and: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(and_expr));
289
290        let expected: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
291            Arc::new(NotExpr::new(col("a", &schema)?)),
292            Operator::Or,
293            Arc::new(NotExpr::new(col("b", &schema)?)),
294        ));
295        assert_not_simplify(&simplifier, not_and, expected);
296
297        Ok(())
298    }
299
300    #[test]
301    fn test_demorgans_law_or() -> Result<()> {
302        let schema = not_test_schema();
303        let simplifier = PhysicalExprSimplifier::new(&schema);
304
305        // NOT(a OR b) -> NOT a AND NOT b
306        let or_expr = Arc::new(BinaryExpr::new(
307            col("a", &schema)?,
308            Operator::Or,
309            col("b", &schema)?,
310        ));
311        let not_or: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(or_expr));
312
313        let expected: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
314            Arc::new(NotExpr::new(col("a", &schema)?)),
315            Operator::And,
316            Arc::new(NotExpr::new(col("b", &schema)?)),
317        ));
318        assert_not_simplify(&simplifier, not_or, expected);
319
320        Ok(())
321    }
322
323    #[test]
324    fn test_demorgans_with_comparison_simplification() -> Result<()> {
325        let schema = not_test_schema();
326        let simplifier = PhysicalExprSimplifier::new(&schema);
327
328        // NOT(c = 1 AND c = 2) -> c != 1 OR c != 2
329        let eq1 = Arc::new(BinaryExpr::new(
330            col("c", &schema)?,
331            Operator::Eq,
332            lit(ScalarValue::Int32(Some(1))),
333        ));
334        let eq2 = Arc::new(BinaryExpr::new(
335            col("c", &schema)?,
336            Operator::Eq,
337            lit(ScalarValue::Int32(Some(2))),
338        ));
339        let and_expr = Arc::new(BinaryExpr::new(eq1, Operator::And, eq2));
340        let not_and: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(and_expr));
341
342        let expected: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
343            Arc::new(BinaryExpr::new(
344                col("c", &schema)?,
345                Operator::NotEq,
346                lit(ScalarValue::Int32(Some(1))),
347            )),
348            Operator::Or,
349            Arc::new(BinaryExpr::new(
350                col("c", &schema)?,
351                Operator::NotEq,
352                lit(ScalarValue::Int32(Some(2))),
353            )),
354        ));
355        assert_not_simplify(&simplifier, not_and, expected);
356
357        Ok(())
358    }
359
360    #[test]
361    fn test_not_of_not_and_not() -> Result<()> {
362        let schema = not_test_schema();
363        let simplifier = PhysicalExprSimplifier::new(&schema);
364
365        // NOT(NOT(a) AND NOT(b)) -> a OR b
366        let not_a = Arc::new(NotExpr::new(col("a", &schema)?));
367        let not_b = Arc::new(NotExpr::new(col("b", &schema)?));
368        let and_expr = Arc::new(BinaryExpr::new(not_a, Operator::And, not_b));
369        let not_and: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(and_expr));
370
371        let expected: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
372            col("a", &schema)?,
373            Operator::Or,
374            col("b", &schema)?,
375        ));
376        assert_not_simplify(&simplifier, not_and, expected);
377
378        Ok(())
379    }
380
381    #[test]
382    fn test_not_in_list() -> Result<()> {
383        let schema = not_test_schema();
384        let simplifier = PhysicalExprSimplifier::new(&schema);
385
386        // NOT(c IN (1, 2, 3)) -> c NOT IN (1, 2, 3)
387        let list = vec![
388            lit(ScalarValue::Int32(Some(1))),
389            lit(ScalarValue::Int32(Some(2))),
390            lit(ScalarValue::Int32(Some(3))),
391        ];
392        let in_list_expr = in_list(col("c", &schema)?, list.clone(), &false, &schema)?;
393        let not_in: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(in_list_expr));
394
395        let expected = in_list(col("c", &schema)?, list, &true, &schema)?;
396        assert_not_simplify(&simplifier, not_in, expected);
397
398        Ok(())
399    }
400
401    #[test]
402    fn test_not_not_in_list() -> Result<()> {
403        let schema = not_test_schema();
404        let simplifier = PhysicalExprSimplifier::new(&schema);
405
406        // NOT(c NOT IN (1, 2, 3)) -> c IN (1, 2, 3)
407        let list = vec![
408            lit(ScalarValue::Int32(Some(1))),
409            lit(ScalarValue::Int32(Some(2))),
410            lit(ScalarValue::Int32(Some(3))),
411        ];
412        let not_in_list_expr = in_list(col("c", &schema)?, list.clone(), &true, &schema)?;
413        let not_not_in: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(not_in_list_expr));
414
415        let expected = in_list(col("c", &schema)?, list, &false, &schema)?;
416        assert_not_simplify(&simplifier, not_not_in, expected);
417
418        Ok(())
419    }
420
421    #[test]
422    fn test_double_not_in_list() -> Result<()> {
423        let schema = not_test_schema();
424        let simplifier = PhysicalExprSimplifier::new(&schema);
425
426        // NOT(NOT(c IN (1, 2, 3))) -> c IN (1, 2, 3)
427        let list = vec![
428            lit(ScalarValue::Int32(Some(1))),
429            lit(ScalarValue::Int32(Some(2))),
430            lit(ScalarValue::Int32(Some(3))),
431        ];
432        let in_list_expr = in_list(col("c", &schema)?, list.clone(), &false, &schema)?;
433        let not_in = Arc::new(NotExpr::new(in_list_expr));
434        let double_not: Arc<dyn PhysicalExpr> = Arc::new(NotExpr::new(not_in));
435
436        let expected = in_list(col("c", &schema)?, list, &false, &schema)?;
437        assert_not_simplify(&simplifier, double_not, expected);
438
439        Ok(())
440    }
441
442    #[test]
443    fn test_deeply_nested_not() -> Result<()> {
444        let schema = not_test_schema();
445        let simplifier = PhysicalExprSimplifier::new(&schema);
446
447        // Create a deeply nested NOT expression: NOT(NOT(NOT(...NOT(c > 5)...)))
448        // This tests that we don't get stack overflow with many nested NOTs.
449        // With recursive_protection enabled (default), this should work by
450        // automatically growing the stack as needed.
451        let inner_expr: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
452            col("c", &schema)?,
453            Operator::Gt,
454            lit(ScalarValue::Int32(Some(5))),
455        ));
456
457        let mut expr = Arc::clone(&inner_expr);
458        // Create 200 layers of NOT to test deep recursion handling
459        for _ in 0..200 {
460            expr = Arc::new(NotExpr::new(expr));
461        }
462
463        // With 200 NOTs (even number), should simplify back to the original expression
464        let expected = inner_expr;
465        assert_not_simplify(&simplifier, Arc::clone(&expr), expected);
466
467        // Manually dismantle the deep input expression to avoid Stack Overflow on Drop
468        // If we just let `expr` go out of scope, Rust's recursive Drop will blow the stack
469        // even with recursive_protection, because Drop doesn't use the #[recursive] attribute.
470        // We peel off layers one by one to avoid deep recursion in Drop.
471        while let Some(not_expr) = expr.as_any().downcast_ref::<NotExpr>() {
472            // Clone the child (Arc increment).
473            // Now child has 2 refs: one in parent, one in `child`.
474            let child = Arc::clone(not_expr.arg());
475
476            // Reassign `expr` to `child`.
477            // This drops the old `expr` (Parent).
478            // Parent refcount -> 0, Parent is dropped.
479            // Parent drops its reference to Child.
480            // Child refcount decrements 2 -> 1.
481            // Child is NOT dropped recursively because we still hold it in `expr`
482            expr = child;
483        }
484
485        Ok(())
486    }
487
488    #[test]
489    fn test_simplify_literal_binary_expr() {
490        let schema = Schema::empty();
491        let simplifier = PhysicalExprSimplifier::new(&schema);
492
493        // 1 + 2 -> 3
494        let expr: Arc<dyn PhysicalExpr> =
495            Arc::new(BinaryExpr::new(lit(1i32), Operator::Plus, lit(2i32)));
496        let result = simplifier.simplify(expr).unwrap();
497        let literal = as_literal(&result);
498        assert_eq!(literal.value(), &ScalarValue::Int32(Some(3)));
499    }
500
501    #[test]
502    fn test_simplify_literal_comparison() {
503        let schema = Schema::empty();
504        let simplifier = PhysicalExprSimplifier::new(&schema);
505
506        // 5 > 3 -> true
507        let expr: Arc<dyn PhysicalExpr> =
508            Arc::new(BinaryExpr::new(lit(5i32), Operator::Gt, lit(3i32)));
509        let result = simplifier.simplify(expr).unwrap();
510        let literal = as_literal(&result);
511        assert_eq!(literal.value(), &ScalarValue::Boolean(Some(true)));
512
513        // 2 > 3 -> false
514        let expr: Arc<dyn PhysicalExpr> =
515            Arc::new(BinaryExpr::new(lit(2i32), Operator::Gt, lit(3i32)));
516        let result = simplifier.simplify(expr).unwrap();
517        let literal = as_literal(&result);
518        assert_eq!(literal.value(), &ScalarValue::Boolean(Some(false)));
519    }
520
521    #[test]
522    fn test_simplify_nested_literal_expr() {
523        let schema = Schema::empty();
524        let simplifier = PhysicalExprSimplifier::new(&schema);
525
526        // (1 + 2) * 3 -> 9
527        let inner: Arc<dyn PhysicalExpr> =
528            Arc::new(BinaryExpr::new(lit(1i32), Operator::Plus, lit(2i32)));
529        let expr: Arc<dyn PhysicalExpr> =
530            Arc::new(BinaryExpr::new(inner, Operator::Multiply, lit(3i32)));
531        let result = simplifier.simplify(expr).unwrap();
532        let literal = as_literal(&result);
533        assert_eq!(literal.value(), &ScalarValue::Int32(Some(9)));
534    }
535
536    #[test]
537    fn test_simplify_deeply_nested_literals() {
538        let schema = Schema::empty();
539        let simplifier = PhysicalExprSimplifier::new(&schema);
540
541        // ((1 + 2) * 3) + ((4 - 1) * 2) -> 9 + 6 -> 15
542        let left: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
543            Arc::new(BinaryExpr::new(lit(1i32), Operator::Plus, lit(2i32))),
544            Operator::Multiply,
545            lit(3i32),
546        ));
547        let right: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
548            Arc::new(BinaryExpr::new(lit(4i32), Operator::Minus, lit(1i32))),
549            Operator::Multiply,
550            lit(2i32),
551        ));
552        let expr: Arc<dyn PhysicalExpr> =
553            Arc::new(BinaryExpr::new(left, Operator::Plus, right));
554        let result = simplifier.simplify(expr).unwrap();
555        let literal = as_literal(&result);
556        assert_eq!(literal.value(), &ScalarValue::Int32(Some(15)));
557    }
558
559    #[test]
560    fn test_no_simplify_with_column() {
561        let schema = test_schema();
562        let simplifier = PhysicalExprSimplifier::new(&schema);
563
564        // c1 + 2 should NOT be simplified (has column reference)
565        let expr: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
566            col("c1", &schema).unwrap(),
567            Operator::Plus,
568            lit(2i32),
569        ));
570        let result = simplifier.simplify(expr).unwrap();
571        // Should remain a BinaryExpr, not become a Literal
572        assert!(result.as_any().downcast_ref::<BinaryExpr>().is_some());
573    }
574
575    #[test]
576    fn test_partial_simplify_with_column() {
577        let schema = test_schema();
578        let simplifier = PhysicalExprSimplifier::new(&schema);
579
580        // (1 + 2) + c1 should simplify the literal part: 3 + c1
581        let literal_part: Arc<dyn PhysicalExpr> =
582            Arc::new(BinaryExpr::new(lit(1i32), Operator::Plus, lit(2i32)));
583        let expr: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
584            literal_part,
585            Operator::Plus,
586            col("c1", &schema).unwrap(),
587        ));
588        let result = simplifier.simplify(expr).unwrap();
589
590        // Should be a BinaryExpr with a Literal(3) on the left
591        let binary = as_binary(&result);
592        let left_literal = as_literal(binary.left());
593        assert_eq!(left_literal.value(), &ScalarValue::Int32(Some(3)));
594    }
595
596    #[test]
597    fn test_simplify_literal_string_concat() {
598        let schema = Schema::empty();
599        let simplifier = PhysicalExprSimplifier::new(&schema);
600
601        // 'hello' || ' world' -> 'hello world'
602        let expr: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new(
603            lit("hello"),
604            Operator::StringConcat,
605            lit(" world"),
606        ));
607        let result = simplifier.simplify(expr).unwrap();
608        let literal = as_literal(&result);
609        assert_eq!(
610            literal.value(),
611            &ScalarValue::Utf8(Some("hello world".to_string()))
612        );
613    }
614}