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 15, 2025
1 parent ffe6514 commit 903c11c
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 3 deletions.
145 changes: 142 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,136 @@ 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(());
}
// If one condition is always true, the AND operation simplifies to the other condition
if rhs_always_true {
*expr = lhs.take_ownership();
return Ok(());
}
if lhs_always_true {
*expr = rhs.take_ownership();
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(());
}
// If one condition is always false, the OR operation simplifies to the other condition
if rhs_always_false {
*expr = lhs.take_ownership();
return Ok(());
}
if lhs_always_false {
*expr = rhs.take_ownership();
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
118 changes: 118 additions & 0 deletions testing/where.test
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,121 @@ do_execsql_test nested-parens-conditionals-and-double-or {
8171|Andrea|Lee|[email protected]|001-594-430-0646|452 Anthony Stravenue|Sandraville|CA|28572|12
9110|Anthony|Barrett|[email protected]|(562)928-9177x8454|86166 Foster Inlet Apt. 284|North Jeffreyburgh|CA|80147|97
9279|Annette|Lynn|[email protected]|(272)700-7181|2676 Laura Points Apt. 683|Tristanville|NY|48646|91}}

# 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}

# 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;
} {}

# 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;
} {{}}

# 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;
} {{}}

0 comments on commit 903c11c

Please sign in to comment.