Skip to content

Commit

Permalink
rewrite_expr: simplify binary expressions whenever possible
Browse files Browse the repository at this point in the history
  • Loading branch information
jussisaurio committed Jan 16, 2025
1 parent bdc06f2 commit a1d0c27
Show file tree
Hide file tree
Showing 2 changed files with 339 additions and 4 deletions.
127 changes: 124 additions & 3 deletions core/translate/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,16 +1004,28 @@ pub fn try_extract_index_search_expression(
}
}

fn expr_true() -> ast::Expr {
ast::Expr::Literal(ast::Literal::Numeric(1.to_string()))
}

fn expr_false() -> ast::Expr {
ast::Expr::Literal(ast::Literal::Numeric(0.to_string()))
}

fn expr_null() -> ast::Expr {
ast::Expr::Literal(ast::Literal::Null)
}

fn rewrite_expr(expr: &mut ast::Expr) -> Result<()> {
match expr {
ast::Expr::Id(id) => {
// Convert "true" and "false" to 1 and 0
if id.0.eq_ignore_ascii_case("true") {
*expr = ast::Expr::Literal(ast::Literal::Numeric(1.to_string()));
*expr = expr_true();
return Ok(());
}
if id.0.eq_ignore_ascii_case("false") {
*expr = ast::Expr::Literal(ast::Literal::Numeric(0.to_string()));
*expr = expr_false();
return Ok(());
}
Ok(())
Expand Down Expand Up @@ -1067,9 +1079,118 @@ fn rewrite_expr(expr: &mut ast::Expr) -> Result<()> {
Ok(())
}
// Process other expressions recursively
ast::Expr::Binary(lhs, _, rhs) => {
ast::Expr::Binary(lhs, op, rhs) => {
rewrite_expr(lhs)?;
rewrite_expr(rhs)?;

if !matches!(
op,
ast::Operator::And
| ast::Operator::Or
| ast::Operator::Equals
| ast::Operator::NotEquals
) {
return Ok(());
}

let lhs_always_true = lhs.is_always_true()?;
let rhs_always_true = rhs.is_always_true()?;

let lhs_always_false = lhs.is_always_false()?;
let rhs_always_false = rhs.is_always_false()?;

let both_always_true = lhs_always_true && rhs_always_true;
let both_always_false = lhs_always_false && rhs_always_false;

let one_always_true = lhs_always_true || rhs_always_true;
let one_always_false = lhs_always_false || rhs_always_false;

let lhs_is_null = **lhs == expr_null();
let rhs_is_null = **rhs == expr_null();
let both_are_null = lhs_is_null && rhs_is_null;
let either_one_is_null = lhs_is_null || rhs_is_null;

let mixed_truth_values =
(lhs_always_true && rhs_always_false) || (lhs_always_false && rhs_always_true);

let null_if_either_is_null = |expr: ast::Expr| {
if either_one_is_null {
expr_null()
} else {
expr
}
};

match op {
ast::Operator::And => {
// If both conditions are always true, the AND operation simplifies to true
if both_always_true {
*expr = expr_true();
return Ok(());
}
// If both conditions are always false, the AND operation simplifies to false (or NULL if _both_ are NULL)
if both_always_false {
*expr = if both_are_null {
expr_null()
} else {
expr_false()
};
return Ok(());
}
// If one condition is always false, the AND operation simplifies to false (or NULL if either is NULL)
if one_always_false {
*expr = null_if_either_is_null(expr_false());
return Ok(());
}
}
ast::Operator::Or => {
// If one condition is always true, the OR operation simplifies to true
if one_always_true {
*expr = expr_true();
return Ok(());
}
// If both conditions are always false, the OR operation simplifies to false (or NULL if either is NULL)
if both_always_false {
*expr = null_if_either_is_null(expr_false());
return Ok(());
}
}
ast::Operator::Equals => {
// If both conditions are always true, == simplifies to true.
if both_always_true {
*expr = expr_true();
return Ok(());
}
// If both conditions are always false, == simplifies to true (or NULL if either is NULL)
if both_always_false {
*expr = null_if_either_is_null(expr_true());
return Ok(());
}
// If one is true and one is false, equality is always false (or NULL if either is NULL)
if mixed_truth_values {
*expr = null_if_either_is_null(expr_false());
return Ok(());
}
}
ast::Operator::NotEquals => {
// If both conditions are always true, != simplifies to false
if both_always_true {
*expr = expr_false();
return Ok(());
}
// If both conditions are always false, != simplifies to false, or NULL if either is NULL
if both_always_false {
*expr = null_if_either_is_null(expr_false());
return Ok(());
}
// If one is true and one is false, inequality is always true (or NULL if either is NULL)
if mixed_truth_values {
*expr = null_if_either_is_null(expr_true());
return Ok(());
}
}
_ => {}
}
Ok(())
}
ast::Expr::FunctionCall { args, .. } => {
Expand Down
216 changes: 215 additions & 1 deletion testing/where.test
Original file line number Diff line number Diff line change
Expand Up @@ -382,4 +382,218 @@ do_execsql_test nested-parens-and-inside-or-regression-test {
AND
(id = 6 OR FALSE)
);
} {1}
} {1}

# Tests for handling of constant expressions in binary comparisons

# AND operator tests
do_execsql_test const-binary-and-true-true {
select true and true;
} {1}

do_execsql_test const-binary-and-true-false {
select true and false;
} {0}

do_execsql_test const-binary-and-false-true {
select false and true;
} {0}

do_execsql_test const-binary-and-false-false {
select false and false;
} {0}

do_execsql_test const-binary-and-true-null {
select true and null;
} {{}}

do_execsql_test const-binary-and-null-true {
select null and true;
} {{}}

do_execsql_test const-binary-and-false-null {
select false and null;
} {0}

do_execsql_test const-binary-and-null-false {
select null and false;
} {0}

do_execsql_test const-binary-and-null-null {
select null and null;
} {{}}

do_execsql_test const-binary-and-truthy-true {
select 2 and true;
} {1}

do_execsql_test const-binary-and-truthy-false {
select 2 and false;
} {0}

do_execsql_test const-binary-and-true-truthy {
select true and 2;
} {1}

do_execsql_test const-binary-and-false-truthy {
select false and 2;
} {0}

do_execsql_test const-binary-and-truthy-null {
select 2 and null;
} {{}}

do_execsql_test const-binary-and-null-truthy {
select null and 2;
} {{}}

# OR operator tests
do_execsql_test const-binary-or-true-true {
select true or true;
} {1}

do_execsql_test const-binary-or-true-false {
select true or false;
} {1}

do_execsql_test const-binary-or-false-true {
select false or true;
} {1}

do_execsql_test const-binary-or-false-false {
select false or false;
} {0}

do_execsql_test const-binary-or-true-null {
select true or null;
} {1}

do_execsql_test const-binary-or-null-true {
select null or true;
} {1}

do_execsql_test const-binary-or-false-null {
select false or null;
} {{}}

do_execsql_test const-binary-or-null-false {
select null or false;
} {{}}

do_execsql_test const-binary-or-null-null {
select null or null;
} {{}}

do_execsql_test const-binary-or-truthy-false {
select 2 or false;
} {1}

do_execsql_test const-binary-or-false-truthy {
select false or 2;
} {1}

do_execsql_test const-binary-or-truthy-true {
select 2 or true;
} {1}

do_execsql_test const-binary-or-true-truthy {
select true or 2;
} {1}

do_execsql_test const-binary-or-truthy-null {
select 2 or null;
} {1}

do_execsql_test const-binary-or-null-truthy {
select null or 2;
} {1}

# Equals operator tests
do_execsql_test const-binary-eq-true-true {
select true = true;
} {1}

do_execsql_test const-binary-eq-false-false {
select false = false;
} {1}

do_execsql_test const-binary-eq-true-false {
select true = false;
} {0}

do_execsql_test const-binary-eq-false-true {
select false = true;
} {0}

do_execsql_test const-binary-eq-null-true {
select null = true;
} {{}}

do_execsql_test const-binary-eq-true-null {
select true = null;
} {{}}

do_execsql_test const-binary-eq-null-null {
select null = null;
} {{}}

do_execsql_test const-binary-eq-truthy-true {
select 1 = true;
} {1}

do_execsql_test const-binary-eq-truthy-false {
select 42 = false;
} {0}

do_execsql_test const-binary-eq-truthy-null {
select 42 = null;
} {{}}

do_execsql_test const-binary-eq-null-truthy {
select null = 42;
} {{}}

# Not equals operator tests
do_execsql_test const-binary-ne-true-true {
select true != true;
} {0}

do_execsql_test const-binary-ne-false-false {
select false != false;
} {0}

do_execsql_test const-binary-ne-true-false {
select true != false;
} {1}

do_execsql_test const-binary-ne-false-true {
select false != true;
} {1}

do_execsql_test const-binary-ne-null-true {
select null != true;
} {{}}

do_execsql_test const-binary-ne-true-null {
select true != null;
} {{}}

do_execsql_test const-binary-ne-null-null {
select null != null;
} {{}}

do_execsql_test const-binary-ne-truthy-true {
select 1 != true;
} {0}

do_execsql_test const-binary-ne-truthy-false {
select 2 != false;
} {1}

do_execsql_test const-binary-ne-truthy-null {
select 42 != null;
} {{}}

do_execsql_test const-binary-ne-null-truthy {
select null != 42;
} {{}}

0 comments on commit a1d0c27

Please sign in to comment.