Skip to main content

radixdb_executor/optimizer/
simplify.rs

1// Copyright 2026 RadixDB Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Expression simplification pass for query optimization
16//!
17//! This module provides expression simplification that runs before planning.
18//! It handles:
19//! - Constant folding (1 + 1 → 2)
20//! - Boolean simplification (TRUE AND x → x, FALSE OR x → x)
21//! - Tautology elimination (1 = 1 → TRUE, x = x → TRUE for NOT NULL cols)
22//! - Contradiction detection (1 = 2 → FALSE)
23//! - Range predicate merging (a > 5 AND a > 3 → a > 5)
24//! - De Morgan's law application where beneficial
25//!
26//! IMPORTANT: This module uses `Option<Expression>` returns to avoid cloning
27//! expressions when no simplification is performed. This is critical for
28//! performance with heavy expressions like EXISTS subqueries.
29
30#![allow(clippy::only_used_in_recursion)]
31
32use radixdb_functions::registry::global_registry;
33use radixdb_functions::FunctionRegistry;
34use radixdb_sql::ast::{
35    BooleanLiteral, Expression, InfixExpression, InfixOperator, IntegerLiteral, PrefixExpression,
36    PrefixOperator,
37};
38use radixdb_sql::token::{Position, Token, TokenType};
39
40/// Expression simplifier that applies optimization rules
41pub struct ExpressionSimplifier<'a> {
42    /// Track if any simplifications were made
43    simplified: bool,
44    _marker: std::marker::PhantomData<&'a FunctionRegistry>,
45}
46
47impl Default for ExpressionSimplifier<'static> {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53impl ExpressionSimplifier<'static> {
54    /// Create a new expression simplifier
55    pub fn new() -> Self {
56        Self::with_registry(global_registry())
57    }
58}
59
60impl<'a> ExpressionSimplifier<'a> {
61    /// Create a simplifier using the executor's authoritative function registry.
62    pub fn with_registry(_function_registry: &'a FunctionRegistry) -> Self {
63        Self {
64            simplified: false,
65            _marker: std::marker::PhantomData,
66        }
67    }
68
69    /// Check if any simplifications were made in the last run
70    pub fn was_simplified(&self) -> bool {
71        self.simplified
72    }
73
74    /// Simplify an expression, applying all optimization rules.
75    /// Returns Some(simplified) if changes were made, None if expression is unchanged.
76    /// This avoids cloning expressions that can't be simplified (like EXISTS subqueries).
77    pub fn try_simplify(&mut self, expr: &Expression) -> Option<Expression> {
78        self.simplified = false;
79        let result = self.simplify_recursive(expr);
80        if self.simplified {
81            Some(result.unwrap_or_else(|| expr.clone()))
82        } else {
83            None
84        }
85    }
86
87    /// Simplify an expression, always returning an Expression.
88    /// Use try_simplify() when you want to avoid cloning unchanged expressions.
89    pub fn simplify(&mut self, expr: &Expression) -> Expression {
90        self.simplified = false;
91        self.simplify_recursive(expr)
92            .unwrap_or_else(|| expr.clone())
93    }
94
95    /// Recursively simplify an expression.
96    /// Returns Some(new_expr) if simplified, None if unchanged.
97    fn simplify_recursive(&mut self, expr: &Expression) -> Option<Expression> {
98        match expr {
99            Expression::Infix(infix) => self.simplify_infix(infix),
100            Expression::Prefix(prefix) => self.simplify_prefix(prefix),
101            Expression::Between(between) => self.simplify_between(between),
102            Expression::In(in_expr) => self.simplify_in(in_expr),
103            // EXISTS and ScalarSubquery contain heavy SelectStatement data.
104            // They can't be simplified to constants, so return None (no clone needed).
105            Expression::Exists(_) | Expression::ScalarSubquery(_) => None,
106            // InHashSet, literals, identifiers, etc. can't be simplified
107            _ => None,
108        }
109    }
110
111    /// Simplify BETWEEN expression
112    fn simplify_between(
113        &mut self,
114        between: &radixdb_sql::ast::BetweenExpression,
115    ) -> Option<Expression> {
116        let value_simplified = self.simplify_recursive(&between.expr);
117        let lower_simplified = self.simplify_recursive(&between.lower);
118        let upper_simplified = self.simplify_recursive(&between.upper);
119
120        let value = value_simplified.as_ref().unwrap_or(&between.expr);
121        let lower = lower_simplified.as_ref().unwrap_or(&between.lower);
122        let upper = upper_simplified.as_ref().unwrap_or(&between.upper);
123
124        // Check for constant BETWEEN
125        if let (
126            Expression::IntegerLiteral(v),
127            Expression::IntegerLiteral(l),
128            Expression::IntegerLiteral(h),
129        ) = (value, lower, upper)
130        {
131            self.simplified = true;
132            let result = v.value >= l.value && v.value <= h.value;
133            return Some(if between.not {
134                self.make_bool(!result)
135            } else {
136                self.make_bool(result)
137            });
138        }
139
140        // If any child was simplified, rebuild the expression
141        if value_simplified.is_some() || lower_simplified.is_some() || upper_simplified.is_some() {
142            Some(Expression::Between(radixdb_sql::ast::BetweenExpression {
143                token: between.token.clone(),
144                expr: Box::new(value_simplified.unwrap_or_else(|| (*between.expr).clone())),
145                lower: Box::new(lower_simplified.unwrap_or_else(|| (*between.lower).clone())),
146                upper: Box::new(upper_simplified.unwrap_or_else(|| (*between.upper).clone())),
147                not: between.not,
148            }))
149        } else {
150            None
151        }
152    }
153
154    /// Simplify IN expression
155    fn simplify_in(&mut self, in_expr: &radixdb_sql::ast::InExpression) -> Option<Expression> {
156        let value_simplified = self.simplify_recursive(&in_expr.left);
157        let list_simplified = self.simplify_recursive(&in_expr.right);
158
159        // If any child was simplified, rebuild the expression
160        if value_simplified.is_some() || list_simplified.is_some() {
161            Some(Expression::In(radixdb_sql::ast::InExpression {
162                token: in_expr.token.clone(),
163                left: Box::new(value_simplified.unwrap_or_else(|| (*in_expr.left).clone())),
164                right: Box::new(list_simplified.unwrap_or_else(|| (*in_expr.right).clone())),
165                not: in_expr.not,
166            }))
167        } else {
168            None
169        }
170    }
171
172    /// Simplify infix (binary) expressions
173    fn simplify_infix(&mut self, infix: &InfixExpression) -> Option<Expression> {
174        // First, recursively simplify children
175        let left_simplified = self.simplify_recursive(&infix.left);
176        let right_simplified = self.simplify_recursive(&infix.right);
177
178        let left = left_simplified.as_ref().unwrap_or(&infix.left);
179        let right = right_simplified.as_ref().unwrap_or(&infix.right);
180
181        // Try operator-specific simplifications
182        let simplified_result = match infix.op_type {
183            InfixOperator::And => self.simplify_and(left, right),
184            InfixOperator::Or => self.simplify_or(left, right),
185            InfixOperator::Equal => self.simplify_equal(left, right),
186            InfixOperator::NotEqual => self.simplify_not_equal(left, right),
187            InfixOperator::LessThan => self.simplify_less_than(left, right),
188            InfixOperator::LessEqual => self.simplify_less_equal(left, right),
189            InfixOperator::GreaterThan => self.simplify_greater_than(left, right),
190            InfixOperator::GreaterEqual => self.simplify_greater_equal(left, right),
191            InfixOperator::Add => self.simplify_add(left, right),
192            InfixOperator::Subtract => self.simplify_subtract(left, right),
193            InfixOperator::Multiply => self.simplify_multiply(left, right),
194            InfixOperator::Divide => self.simplify_divide(left, right),
195            _ => None,
196        };
197
198        if let Some(result) = simplified_result {
199            self.simplified = true;
200            return Some(result);
201        }
202
203        // If children were simplified but operator wasn't, rebuild with simplified children
204        if left_simplified.is_some() || right_simplified.is_some() {
205            Some(Expression::Infix(InfixExpression {
206                token: infix.token.clone(),
207                left: Box::new(left_simplified.unwrap_or_else(|| (*infix.left).clone())),
208                operator: infix.operator.clone(),
209                op_type: infix.op_type,
210                right: Box::new(right_simplified.unwrap_or_else(|| (*infix.right).clone())),
211            }))
212        } else {
213            None
214        }
215    }
216
217    /// Simplify AND expressions
218    fn simplify_and(&self, left: &Expression, right: &Expression) -> Option<Expression> {
219        // TRUE AND x → x
220        if self.is_always_true(left) {
221            return Some(right.clone());
222        }
223        // x AND TRUE → x
224        if self.is_always_true(right) {
225            return Some(left.clone());
226        }
227        // FALSE AND x → FALSE
228        if self.is_always_false(left) {
229            return Some(left.clone());
230        }
231        // x AND FALSE → FALSE
232        if self.is_always_false(right) {
233            return Some(right.clone());
234        }
235        // x AND x → x
236        if self.expr_equals(left, right) {
237            return Some(left.clone());
238        }
239        // Try to merge range predicates: a > 5 AND a > 3 → a > 5
240        self.try_merge_range_predicates(left, right)
241    }
242
243    /// Simplify OR expressions
244    fn simplify_or(&self, left: &Expression, right: &Expression) -> Option<Expression> {
245        // FALSE OR x → x
246        if self.is_always_false(left) {
247            return Some(right.clone());
248        }
249        // x OR FALSE → x
250        if self.is_always_false(right) {
251            return Some(left.clone());
252        }
253        // TRUE OR x → TRUE
254        if self.is_always_true(left) {
255            return Some(left.clone());
256        }
257        // x OR TRUE → TRUE
258        if self.is_always_true(right) {
259            return Some(right.clone());
260        }
261        // x OR x → x
262        if self.expr_equals(left, right) {
263            return Some(left.clone());
264        }
265        None
266    }
267
268    /// Simplify equality expressions
269    fn simplify_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
270        // Constant comparison: 1 = 1 → TRUE, 1 = 2 → FALSE
271        if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::Equal) {
272            return Some(self.make_bool(result));
273        }
274        None
275    }
276
277    /// Simplify not-equal expressions
278    fn simplify_not_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
279        if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::NotEqual) {
280            return Some(self.make_bool(result));
281        }
282        None
283    }
284
285    /// Simplify less-than expressions
286    fn simplify_less_than(&self, left: &Expression, right: &Expression) -> Option<Expression> {
287        if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::LessThan) {
288            return Some(self.make_bool(result));
289        }
290        None
291    }
292
293    /// Simplify less-equal expressions
294    fn simplify_less_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
295        if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::LessEqual) {
296            return Some(self.make_bool(result));
297        }
298        None
299    }
300
301    /// Simplify greater-than expressions
302    fn simplify_greater_than(&self, left: &Expression, right: &Expression) -> Option<Expression> {
303        if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::GreaterThan) {
304            return Some(self.make_bool(result));
305        }
306        None
307    }
308
309    /// Simplify greater-equal expressions
310    fn simplify_greater_equal(&self, left: &Expression, right: &Expression) -> Option<Expression> {
311        if let Some(result) = self.try_eval_comparison(left, right, InfixOperator::GreaterEqual) {
312            return Some(self.make_bool(result));
313        }
314        None
315    }
316
317    /// Simplify addition expressions
318    fn simplify_add(&self, left: &Expression, right: &Expression) -> Option<Expression> {
319        self.try_eval_arithmetic(left, right, InfixOperator::Add)
320    }
321
322    /// Simplify subtraction expressions
323    fn simplify_subtract(&self, left: &Expression, right: &Expression) -> Option<Expression> {
324        self.try_eval_arithmetic(left, right, InfixOperator::Subtract)
325    }
326
327    /// Simplify multiplication expressions
328    fn simplify_multiply(&self, left: &Expression, right: &Expression) -> Option<Expression> {
329        self.try_eval_arithmetic(left, right, InfixOperator::Multiply)
330    }
331
332    /// Simplify division expressions
333    fn simplify_divide(&self, left: &Expression, right: &Expression) -> Option<Expression> {
334        self.try_eval_arithmetic(left, right, InfixOperator::Divide)
335    }
336
337    /// Simplify prefix (unary) expressions
338    fn simplify_prefix(&mut self, prefix: &PrefixExpression) -> Option<Expression> {
339        let operand_simplified = self.simplify_recursive(&prefix.right);
340        let operand = operand_simplified.as_ref().unwrap_or(&prefix.right);
341
342        let simplified_result = match prefix.op_type {
343            PrefixOperator::Not => {
344                // NOT TRUE → FALSE
345                if self.is_always_true(operand) {
346                    Some(self.make_bool(false))
347                }
348                // NOT FALSE → TRUE
349                else if self.is_always_false(operand) {
350                    Some(self.make_bool(true))
351                }
352                // NOT NOT x → x
353                else if let Expression::Prefix(inner) = operand {
354                    if inner.op_type == PrefixOperator::Not {
355                        Some((*inner.right).clone())
356                    } else {
357                        None
358                    }
359                } else {
360                    None
361                }
362            }
363            PrefixOperator::Negate => {
364                // -(constant) → constant negated
365                if let Expression::IntegerLiteral(lit) = operand {
366                    lit.value.checked_neg().map(|value| self.make_int(value))
367                } else {
368                    None
369                }
370            }
371            PrefixOperator::Plus => {
372                // Unary plus is only total for a known numeric literal.
373                match operand {
374                    Expression::IntegerLiteral(_) | Expression::FloatLiteral(_) => {
375                        Some(operand.clone())
376                    }
377                    _ => None,
378                }
379            }
380            _ => None,
381        };
382
383        if let Some(result) = simplified_result {
384            self.simplified = true;
385            return Some(result);
386        }
387
388        // If operand was simplified, rebuild the expression
389        operand_simplified.map(|simplified_operand| {
390            Expression::Prefix(PrefixExpression {
391                token: prefix.token.clone(),
392                operator: prefix.operator.clone(),
393                op_type: prefix.op_type,
394                right: Box::new(simplified_operand),
395            })
396        })
397    }
398
399    /// Check if expression is always TRUE
400    fn is_always_true(&self, expr: &Expression) -> bool {
401        match expr {
402            Expression::BooleanLiteral(b) => b.value,
403            _ => false,
404        }
405    }
406
407    /// Check if expression is always FALSE
408    fn is_always_false(&self, expr: &Expression) -> bool {
409        match expr {
410            Expression::BooleanLiteral(b) => !b.value,
411            Expression::Infix(infix) => {
412                // 1 = 2 is always false
413                if infix.op_type == InfixOperator::Equal {
414                    if let (Expression::IntegerLiteral(l), Expression::IntegerLiteral(r)) =
415                        (infix.left.as_ref(), infix.right.as_ref())
416                    {
417                        return l.value != r.value;
418                    }
419                }
420                false
421            }
422            _ => false,
423        }
424    }
425
426    /// Check if two expressions are structurally equal
427    fn expr_equals(&self, a: &Expression, b: &Expression) -> bool {
428        match (a, b) {
429            (Expression::Identifier(a), Expression::Identifier(b)) => a.value == b.value,
430            (Expression::QualifiedIdentifier(a), Expression::QualifiedIdentifier(b)) => {
431                a.qualifier.value == b.qualifier.value && a.name.value == b.name.value
432            }
433            (Expression::IntegerLiteral(a), Expression::IntegerLiteral(b)) => a.value == b.value,
434            (Expression::FloatLiteral(a), Expression::FloatLiteral(b)) => a.value == b.value,
435            (Expression::StringLiteral(a), Expression::StringLiteral(b)) => a.value == b.value,
436            (Expression::BooleanLiteral(a), Expression::BooleanLiteral(b)) => a.value == b.value,
437            (Expression::NullLiteral(_), Expression::NullLiteral(_)) => true,
438            (Expression::Infix(a), Expression::Infix(b)) => {
439                a.op_type == b.op_type
440                    && self.expr_equals(&a.left, &b.left)
441                    && self.expr_equals(&a.right, &b.right)
442            }
443            (Expression::Prefix(a), Expression::Prefix(b)) => {
444                a.op_type == b.op_type && self.expr_equals(&a.right, &b.right)
445            }
446            _ => false,
447        }
448    }
449
450    /// Try to evaluate a comparison between constants
451    fn try_eval_comparison(
452        &self,
453        left: &Expression,
454        right: &Expression,
455        op: InfixOperator,
456    ) -> Option<bool> {
457        match (left, right) {
458            (Expression::IntegerLiteral(l), Expression::IntegerLiteral(r)) => Some(match op {
459                InfixOperator::Equal => l.value == r.value,
460                InfixOperator::NotEqual => l.value != r.value,
461                InfixOperator::LessThan => l.value < r.value,
462                InfixOperator::LessEqual => l.value <= r.value,
463                InfixOperator::GreaterThan => l.value > r.value,
464                InfixOperator::GreaterEqual => l.value >= r.value,
465                _ => return None,
466            }),
467            (Expression::FloatLiteral(l), Expression::FloatLiteral(r)) => {
468                let left = radixdb_core::Value::Float(l.value);
469                let right = radixdb_core::Value::Float(r.value);
470                Some(match op {
471                    InfixOperator::Equal => left == right,
472                    InfixOperator::NotEqual => left != right,
473                    InfixOperator::LessThan => left < right,
474                    InfixOperator::LessEqual => left <= right,
475                    InfixOperator::GreaterThan => left > right,
476                    InfixOperator::GreaterEqual => left >= right,
477                    _ => return None,
478                })
479            }
480            (Expression::StringLiteral(l), Expression::StringLiteral(r)) => Some(match op {
481                InfixOperator::Equal => l.value == r.value,
482                InfixOperator::NotEqual => l.value != r.value,
483                InfixOperator::LessThan => l.value < r.value,
484                InfixOperator::LessEqual => l.value <= r.value,
485                InfixOperator::GreaterThan => l.value > r.value,
486                InfixOperator::GreaterEqual => l.value >= r.value,
487                _ => return None,
488            }),
489            (Expression::BooleanLiteral(l), Expression::BooleanLiteral(r)) => Some(match op {
490                InfixOperator::Equal => l.value == r.value,
491                InfixOperator::NotEqual => l.value != r.value,
492                _ => return None,
493            }),
494            _ => None,
495        }
496    }
497
498    /// Try to evaluate arithmetic on constants
499    fn try_eval_arithmetic(
500        &self,
501        left: &Expression,
502        right: &Expression,
503        op: InfixOperator,
504    ) -> Option<Expression> {
505        match (left, right) {
506            (Expression::IntegerLiteral(l), Expression::IntegerLiteral(r)) => {
507                let result = match op {
508                    InfixOperator::Add => l.value.checked_add(r.value)?,
509                    InfixOperator::Subtract => l.value.checked_sub(r.value)?,
510                    InfixOperator::Multiply => l.value.checked_mul(r.value)?,
511                    InfixOperator::Divide => {
512                        if r.value == 0 {
513                            return None;
514                        }
515                        l.value.checked_div(r.value)?
516                    }
517                    InfixOperator::Modulo => {
518                        if r.value == 0 {
519                            return None;
520                        }
521                        l.value.checked_rem(r.value)?
522                    }
523                    _ => return None,
524                };
525                Some(self.make_int(result))
526            }
527            _ => None,
528        }
529    }
530
531    /// Try to merge overlapping range predicates
532    /// a > 5 AND a > 3 → a > 5
533    /// a < 5 AND a < 10 → a < 5
534    fn try_merge_range_predicates(
535        &self,
536        left: &Expression,
537        right: &Expression,
538    ) -> Option<Expression> {
539        let (left_infix, right_infix) = match (left, right) {
540            (Expression::Infix(l), Expression::Infix(r)) => (l, r),
541            _ => return None,
542        };
543
544        // Check if both are comparisons on the same column
545        if !self.same_column(&left_infix.left, &right_infix.left) {
546            return None;
547        }
548
549        // Check for constant right-hand sides
550        let left_val = self.extract_int_literal(&left_infix.right)?;
551        let right_val = self.extract_int_literal(&right_infix.right)?;
552
553        // Merge based on operator types
554        match (left_infix.op_type, right_infix.op_type) {
555            // a > 5 AND a > 3 → a > 5 (keep larger)
556            (InfixOperator::GreaterThan, InfixOperator::GreaterThan)
557            | (InfixOperator::GreaterEqual, InfixOperator::GreaterEqual) => {
558                if left_val >= right_val {
559                    Some(left.clone())
560                } else {
561                    Some(right.clone())
562                }
563            }
564            // a < 5 AND a < 10 → a < 5 (keep smaller)
565            (InfixOperator::LessThan, InfixOperator::LessThan)
566            | (InfixOperator::LessEqual, InfixOperator::LessEqual) => {
567                if left_val <= right_val {
568                    Some(left.clone())
569                } else {
570                    Some(right.clone())
571                }
572            }
573            // a > 5 AND a >= 5 → a > 5 (stricter)
574            (InfixOperator::GreaterThan, InfixOperator::GreaterEqual) if left_val == right_val => {
575                Some(left.clone())
576            }
577            (InfixOperator::GreaterEqual, InfixOperator::GreaterThan) if left_val == right_val => {
578                Some(right.clone())
579            }
580            // a < 5 AND a <= 5 → a < 5 (stricter)
581            (InfixOperator::LessThan, InfixOperator::LessEqual) if left_val == right_val => {
582                Some(left.clone())
583            }
584            (InfixOperator::LessEqual, InfixOperator::LessThan) if left_val == right_val => {
585                Some(right.clone())
586            }
587            _ => None,
588        }
589    }
590
591    fn same_column(&self, left: &Expression, right: &Expression) -> bool {
592        match (left, right) {
593            (Expression::Identifier(left), Expression::Identifier(right)) => {
594                Self::same_identifier(left, right)
595            }
596            (Expression::QualifiedIdentifier(left), Expression::QualifiedIdentifier(right)) => {
597                Self::same_identifier(&left.qualifier, &right.qualifier)
598                    && Self::same_identifier(&left.name, &right.name)
599            }
600            _ => false,
601        }
602    }
603
604    fn same_identifier(
605        left: &radixdb_sql::ast::Identifier,
606        right: &radixdb_sql::ast::Identifier,
607    ) -> bool {
608        left.token.quoted == right.token.quoted
609            && if left.token.quoted {
610                left.value == right.value
611            } else {
612                left.value_lower == right.value_lower
613            }
614    }
615
616    /// Extract integer literal value
617    fn extract_int_literal(&self, expr: &Expression) -> Option<i64> {
618        match expr {
619            Expression::IntegerLiteral(lit) => Some(lit.value),
620            _ => None,
621        }
622    }
623
624    /// Create a boolean literal expression
625    fn make_bool(&self, value: bool) -> Expression {
626        Expression::BooleanLiteral(BooleanLiteral {
627            token: Token::new(
628                TokenType::Keyword,
629                if value { "TRUE" } else { "FALSE" },
630                Position::default(),
631            ),
632            value,
633        })
634    }
635
636    /// Create an integer literal expression
637    fn make_int(&self, value: i64) -> Expression {
638        Expression::IntegerLiteral(IntegerLiteral {
639            token: Token::new(TokenType::Integer, value.to_string(), Position::default()),
640            value,
641        })
642    }
643}
644
645/// Convenience function to simplify an expression
646pub fn simplify_expression(expr: &Expression) -> Expression {
647    let mut simplifier = ExpressionSimplifier::new();
648    simplifier.simplify(expr)
649}
650
651/// Repeatedly simplify until no more changes
652pub fn simplify_expression_fixed_point(expr: &Expression) -> Expression {
653    let mut simplifier = ExpressionSimplifier::new();
654    let mut current = expr.clone();
655    let mut iterations = 0;
656    const MAX_ITERATIONS: usize = 10;
657
658    loop {
659        let simplified = simplifier.try_simplify(&current);
660        if simplified.is_none() || iterations >= MAX_ITERATIONS {
661            break;
662        }
663        current = simplified.unwrap();
664        iterations += 1;
665    }
666
667    current
668}
669
670#[cfg(test)]
671mod tests {
672    use super::*;
673    use radixdb_sql::ast::{Identifier, QualifiedIdentifier};
674
675    fn make_int_lit(value: i64) -> Expression {
676        Expression::IntegerLiteral(IntegerLiteral {
677            token: Token::new(TokenType::Integer, value.to_string(), Position::default()),
678            value,
679        })
680    }
681
682    fn make_bool_lit(value: bool) -> Expression {
683        Expression::BooleanLiteral(BooleanLiteral {
684            token: Token::new(
685                TokenType::Keyword,
686                if value { "TRUE" } else { "FALSE" },
687                Position::default(),
688            ),
689            value,
690        })
691    }
692
693    fn make_float_lit(value: f64) -> Expression {
694        Expression::FloatLiteral(radixdb_sql::ast::FloatLiteral {
695            token: Token::new(TokenType::Float, value.to_string(), Position::default()),
696            value,
697        })
698    }
699
700    fn make_identifier(name: &str) -> Expression {
701        Expression::Identifier(Identifier::new(
702            Token::new(TokenType::Identifier, name, Position::default()),
703            name.to_string(),
704        ))
705    }
706
707    fn make_qualified_identifier(qualifier: &str, name: &str) -> Expression {
708        Expression::QualifiedIdentifier(QualifiedIdentifier {
709            token: Token::new(
710                TokenType::Identifier,
711                format!("{qualifier}.{name}"),
712                Position::default(),
713            ),
714            qualifier: Box::new(Identifier::new(
715                Token::new(TokenType::Identifier, qualifier, Position::default()),
716                qualifier.to_string(),
717            )),
718            intermediate: None,
719            name: Box::new(Identifier::new(
720                Token::new(TokenType::Identifier, name, Position::default()),
721                name.to_string(),
722            )),
723        })
724    }
725
726    fn make_prefix(op: PrefixOperator, right: Expression) -> Expression {
727        let operator = match op {
728            PrefixOperator::Negate => "-",
729            PrefixOperator::Plus => "+",
730            PrefixOperator::Not => "NOT",
731            _ => "?",
732        };
733        Expression::Prefix(PrefixExpression {
734            token: Token::new(TokenType::Operator, operator, Position::default()),
735            operator: operator.into(),
736            op_type: op,
737            right: Box::new(right),
738        })
739    }
740
741    fn make_infix(left: Expression, op: InfixOperator, right: Expression) -> Expression {
742        let op_str = match op {
743            InfixOperator::And => "AND",
744            InfixOperator::Or => "OR",
745            InfixOperator::Equal => "=",
746            InfixOperator::NotEqual => "<>",
747            InfixOperator::LessThan => "<",
748            InfixOperator::GreaterThan => ">",
749            InfixOperator::Add => "+",
750            InfixOperator::Subtract => "-",
751            InfixOperator::Multiply => "*",
752            _ => "?",
753        };
754        Expression::Infix(InfixExpression {
755            token: Token::new(TokenType::Operator, op_str, Position::default()),
756            left: Box::new(left),
757            operator: op_str.into(),
758            op_type: op,
759            right: Box::new(right),
760        })
761    }
762
763    #[test]
764    fn test_constant_folding_arithmetic() {
765        let expr = make_infix(make_int_lit(2), InfixOperator::Add, make_int_lit(3));
766        let result = simplify_expression(&expr);
767
768        if let Expression::IntegerLiteral(lit) = result {
769            assert_eq!(lit.value, 5);
770        } else {
771            panic!("Expected IntegerLiteral");
772        }
773    }
774
775    #[test]
776    fn test_constant_folding_comparison() {
777        // 1 = 1 → TRUE
778        let expr = make_infix(make_int_lit(1), InfixOperator::Equal, make_int_lit(1));
779        let result = simplify_expression(&expr);
780
781        if let Expression::BooleanLiteral(lit) = result {
782            assert!(lit.value);
783        } else {
784            panic!("Expected BooleanLiteral(true)");
785        }
786
787        // 1 = 2 → FALSE
788        let expr = make_infix(make_int_lit(1), InfixOperator::Equal, make_int_lit(2));
789        let result = simplify_expression(&expr);
790
791        if let Expression::BooleanLiteral(lit) = result {
792            assert!(!lit.value);
793        } else {
794            panic!("Expected BooleanLiteral(false)");
795        }
796    }
797
798    #[test]
799    fn test_boolean_and_simplification() {
800        // TRUE AND x → x
801        let x = make_identifier("x");
802        let expr = make_infix(make_bool_lit(true), InfixOperator::And, x.clone());
803        let result = simplify_expression(&expr);
804
805        if let Expression::Identifier(id) = result {
806            assert_eq!(id.value, "x");
807        } else {
808            panic!("Expected Identifier 'x'");
809        }
810
811        // FALSE AND x → FALSE
812        let expr = make_infix(make_bool_lit(false), InfixOperator::And, x.clone());
813        let result = simplify_expression(&expr);
814
815        if let Expression::BooleanLiteral(lit) = result {
816            assert!(!lit.value);
817        } else {
818            panic!("Expected BooleanLiteral(false)");
819        }
820    }
821
822    #[test]
823    fn test_boolean_or_simplification() {
824        // FALSE OR x → x
825        let x = make_identifier("x");
826        let expr = make_infix(make_bool_lit(false), InfixOperator::Or, x.clone());
827        let result = simplify_expression(&expr);
828
829        if let Expression::Identifier(id) = result {
830            assert_eq!(id.value, "x");
831        } else {
832            panic!("Expected Identifier 'x'");
833        }
834
835        // TRUE OR x → TRUE
836        let expr = make_infix(make_bool_lit(true), InfixOperator::Or, x.clone());
837        let result = simplify_expression(&expr);
838
839        if let Expression::BooleanLiteral(lit) = result {
840            assert!(lit.value);
841        } else {
842            panic!("Expected BooleanLiteral(true)");
843        }
844    }
845
846    #[test]
847    fn test_arithmetic_identity() {
848        let x = make_identifier("x");
849
850        // Schema-free arithmetic identities can suppress type/NULL errors.
851        let expr = make_infix(x.clone(), InfixOperator::Add, make_int_lit(0));
852        let result = simplify_expression(&expr);
853        assert_eq!(result, expr);
854
855        let expr = make_infix(x.clone(), InfixOperator::Multiply, make_int_lit(1));
856        let result = simplify_expression(&expr);
857        assert_eq!(result, expr);
858
859        // x * 0 must be preserved without schema/nullability proof.
860        let expr = make_infix(x.clone(), InfixOperator::Multiply, make_int_lit(0));
861        let result = simplify_expression(&expr);
862        assert_eq!(result, expr);
863    }
864
865    #[test]
866    fn test_range_predicate_merge() {
867        let a = make_identifier("a");
868
869        // a > 5 AND a > 3 → a > 5
870        let left = make_infix(a.clone(), InfixOperator::GreaterThan, make_int_lit(5));
871        let right = make_infix(a.clone(), InfixOperator::GreaterThan, make_int_lit(3));
872        let expr = make_infix(left, InfixOperator::And, right);
873        let result = simplify_expression(&expr);
874
875        // Should keep the stricter condition (a > 5)
876        if let Expression::Infix(infix) = result {
877            if let Expression::IntegerLiteral(lit) = infix.right.as_ref() {
878                assert_eq!(lit.value, 5);
879            } else {
880                panic!("Expected IntegerLiteral(5)");
881            }
882        } else {
883            panic!("Expected Infix expression");
884        }
885    }
886
887    #[test]
888    fn test_idempotent_and() {
889        let x = make_identifier("x");
890
891        // x AND x → x
892        let expr = make_infix(x.clone(), InfixOperator::And, x.clone());
893        let result = simplify_expression(&expr);
894        assert!(matches!(result, Expression::Identifier(_)));
895    }
896
897    #[test]
898    fn test_idempotent_or() {
899        let x = make_identifier("x");
900
901        // x OR x → x
902        let expr = make_infix(x.clone(), InfixOperator::Or, x.clone());
903        let result = simplify_expression(&expr);
904        assert!(matches!(result, Expression::Identifier(_)));
905    }
906
907    #[test]
908    fn test_self_comparison() {
909        let x = make_identifier("x");
910
911        // Self-comparisons depend on NULL/type semantics.
912        let expr = make_infix(x.clone(), InfixOperator::Equal, x.clone());
913        let result = simplify_expression(&expr);
914        assert_eq!(result, expr);
915
916        // Ordering self-comparisons have the same dependency.
917        let expr = make_infix(x.clone(), InfixOperator::LessThan, x.clone());
918        let result = simplify_expression(&expr);
919        assert_eq!(result, expr);
920    }
921
922    #[test]
923    fn test_subtraction_identity() {
924        let x = make_identifier("x");
925
926        // x - x depends on type/nullability and must remain checked at runtime.
927        let expr = make_infix(x.clone(), InfixOperator::Subtract, x.clone());
928        let result = simplify_expression(&expr);
929        assert_eq!(result, expr);
930    }
931
932    #[test]
933    fn test_no_clone_for_unchanged() {
934        // Test that try_simplify returns None for expressions that can't be simplified
935        let mut simplifier = ExpressionSimplifier::new();
936        let x = make_identifier("x");
937
938        // Simple identifier can't be simplified
939        let result = simplifier.try_simplify(&x);
940        assert!(result.is_none());
941        assert!(!simplifier.was_simplified());
942    }
943
944    #[test]
945    fn test_r5_l03_simplifier_preserves_unproven_semantics() {
946        let x = make_identifier("x");
947        let schema_dependent = [
948            make_infix(x.clone(), InfixOperator::Equal, x.clone()),
949            make_infix(x.clone(), InfixOperator::NotEqual, x.clone()),
950            make_infix(x.clone(), InfixOperator::Subtract, x.clone()),
951            make_infix(x.clone(), InfixOperator::Multiply, make_int_lit(0)),
952        ];
953        for expression in schema_dependent {
954            assert_eq!(simplify_expression(&expression), expression);
955        }
956
957        let double_min = make_prefix(
958            PrefixOperator::Negate,
959            make_prefix(PrefixOperator::Negate, make_int_lit(i64::MIN)),
960        );
961        let simplified = std::panic::catch_unwind(|| simplify_expression(&double_min));
962        assert_eq!(simplified.ok(), Some(double_min));
963
964        let left = make_infix(
965            make_qualified_identifier("a", "id"),
966            InfixOperator::GreaterThan,
967            make_int_lit(5),
968        );
969        let right = make_infix(
970            make_qualified_identifier("b", "id"),
971            InfixOperator::GreaterThan,
972            make_int_lit(3),
973        );
974        let qualified_range = make_infix(left, InfixOperator::And, right);
975        assert_eq!(simplify_expression(&qualified_range), qualified_range);
976    }
977
978    #[test]
979    fn v2_r5_float_folding_uses_canonical_nan_and_zero_contract() {
980        let nan_a = make_float_lit(f64::from_bits(0x7ff8_0000_0000_0001));
981        let nan_b = make_float_lit(f64::from_bits(0x7ff8_0000_0000_0002));
982        let equal = simplify_expression(&make_infix(
983            nan_a.clone(),
984            InfixOperator::Equal,
985            nan_b.clone(),
986        ));
987        assert!(matches!(
988            equal,
989            Expression::BooleanLiteral(BooleanLiteral { value: true, .. })
990        ));
991
992        let less = simplify_expression(&make_infix(
993            make_float_lit(1.0),
994            InfixOperator::LessThan,
995            nan_a,
996        ));
997        assert!(matches!(
998            less,
999            Expression::BooleanLiteral(BooleanLiteral { value: true, .. })
1000        ));
1001
1002        let zero_equal = simplify_expression(&make_infix(
1003            make_float_lit(-0.0),
1004            InfixOperator::Equal,
1005            make_float_lit(0.0),
1006        ));
1007        assert!(matches!(
1008            zero_equal,
1009            Expression::BooleanLiteral(BooleanLiteral { value: true, .. })
1010        ));
1011    }
1012}