diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 958005259..6a9b7040b 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -4,7 +4,7 @@ use limbo_sqlite3_parser::ast::{self, UnaryOperator}; use crate::function::JsonFunc; use crate::function::{Func, FuncCtx, MathFuncArity, ScalarFunc, VectorFunc}; use crate::schema::{Table, Type}; -use crate::util::normalize_ident; +use crate::util::{exprs_are_equivalent, normalize_ident}; use crate::vdbe::{ builder::ProgramBuilder, insn::{CmpInsFlags, Insn}, @@ -494,8 +494,8 @@ pub fn translate_expr( match expr { ast::Expr::Between { .. } => todo!(), ast::Expr::Binary(e1, op, e2) => { - // Check if both sides of the expression are identical and reuse the same register if so - if e1 == e2 { + // Check if both sides of the expression are equivalent and reuse the same register if so + if exprs_are_equivalent(e1, e2) { let shared_reg = program.alloc_register(); translate_expr(program, referenced_tables, e1, shared_reg, resolver)?; diff --git a/core/util.rs b/core/util.rs index 3d12a2c6e..80a52f387 100644 --- a/core/util.rs +++ b/core/util.rs @@ -399,7 +399,11 @@ pub fn exprs_are_equivalent(expr1: &Expr, expr2: &Expr) -> bool { (Expr::Unary(op1, expr1), Expr::Unary(op2, expr2)) => { op1 == op2 && exprs_are_equivalent(expr1, expr2) } - (Expr::Variable(var1), Expr::Variable(var2)) => var1 == var2, + // Variables that are not bound to a specific value, are treated as NULL + // https://sqlite.org/lang_expr.html#varparam + (Expr::Variable(var), Expr::Variable(var2)) if var == "" && var2 == "" => false, + // Named variables can be compared by their name + (Expr::Variable(val), Expr::Variable(val2)) => val == val2, (Expr::Parenthesized(exprs1), Expr::Parenthesized(exprs2)) => { exprs1.len() == exprs2.len() && exprs1 @@ -945,6 +949,24 @@ pub mod tests { assert_eq!(normalize_ident("\"foo\""), "foo"); } + #[test] + fn test_anonymous_variable_comparison() { + let expr1 = Expr::Variable("".to_string()); + let expr2 = Expr::Variable("".to_string()); + assert!(!exprs_are_equivalent(&expr1, &expr2)); + } + + #[test] + fn test_named_variable_comparison() { + let expr1 = Expr::Variable("1".to_string()); + let expr2 = Expr::Variable("1".to_string()); + assert!(exprs_are_equivalent(&expr1, &expr2)); + + let expr1 = Expr::Variable("1".to_string()); + let expr2 = Expr::Variable("2".to_string()); + assert!(!exprs_are_equivalent(&expr1, &expr2)); + } + #[test] fn test_basic_addition_exprs_are_equivalent() { let expr1 = Expr::Binary(