diff --git a/COMPAT.md b/COMPAT.md index b476bc3f0..f7bbb963a 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -493,7 +493,7 @@ Modifiers: | OpenWrite | No | | OpenWriteAsync | Yes | | OpenWriteAwait | Yes | -| Or | No | +| Or | Yes | | Pagecount | No | | Param | No | | ParseSchema | No | diff --git a/core/translate/expr.rs b/core/translate/expr.rs index d522ddec2..abec8b9a8 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -612,6 +612,13 @@ pub fn translate_expr( dest: target_register, }); } + ast::Operator::Or => { + program.emit_insn(Insn::Or { + lhs: e1_reg, + rhs: e2_reg, + dest: target_register, + }); + } ast::Operator::BitwiseAnd => { program.emit_insn(Insn::BitAnd { lhs: e1_reg, diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 40d6f25e9..89967608a 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1129,6 +1129,15 @@ pub fn insn_to_str( 0, format!("r[{}]=(r[{}] && r[{}])", dest, lhs, rhs), ), + Insn::Or { lhs, rhs, dest } => ( + "Or", + *rhs as i32, + *lhs as i32, + *dest as i32, + OwnedValue::build_text(Rc::new("".to_string())), + 0, + format!("r[{}]=(r[{}] || r[{}])", dest, lhs, rhs), + ), }; format!( "{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}", diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 4e393116c..3fdd02a14 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -557,6 +557,12 @@ pub enum Insn { rhs: usize, dest: usize, }, + /// Take the logical OR of the values in register P1 and P2 and store the answer in register P3. + Or { + lhs: usize, + rhs: usize, + dest: usize, + }, } fn cast_text_to_numerical(value: &str) -> OwnedValue { @@ -980,9 +986,43 @@ pub fn exec_and(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { } } +pub fn exec_or(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { + if let OwnedValue::Agg(agg) = lhs { + lhs = agg.final_value(); + } + if let OwnedValue::Agg(agg) = rhs { + rhs = agg.final_value(); + } + + match (lhs, rhs) { + (OwnedValue::Null, OwnedValue::Null) + | (OwnedValue::Null, OwnedValue::Float(0.0)) + | (OwnedValue::Float(0.0), OwnedValue::Null) + | (OwnedValue::Null, OwnedValue::Integer(0)) + | (OwnedValue::Integer(0), OwnedValue::Null) => OwnedValue::Null, + (OwnedValue::Float(0.0), OwnedValue::Integer(0)) + | (OwnedValue::Integer(0), OwnedValue::Float(0.0)) + | (OwnedValue::Float(0.0), OwnedValue::Float(0.0)) + | (OwnedValue::Integer(0), OwnedValue::Integer(0)) => OwnedValue::Integer(0), + (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_or( + &cast_text_to_numerical(&lhs.value), + &cast_text_to_numerical(&rhs.value), + ), + (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { + exec_or(&cast_text_to_numerical(&text.value), other) + } + _ => OwnedValue::Integer(1), + } +} + #[cfg(test)] mod tests { - use crate::types::OwnedValue; + use std::rc::Rc; + + use crate::{ + types::{LimboText, OwnedValue}, + vdbe::insn::exec_or, + }; use super::exec_and; @@ -1012,4 +1052,54 @@ mod tests { assert_eq!(exec_and(lhs, rhs), outpus[i]); } } + + #[test] + fn test_exec_or() { + let inputs = vec![ + (OwnedValue::Integer(0), OwnedValue::Null), + (OwnedValue::Null, OwnedValue::Integer(1)), + (OwnedValue::Null, OwnedValue::Null), + (OwnedValue::Float(0.0), OwnedValue::Null), + (OwnedValue::Integer(1), OwnedValue::Float(2.2)), + (OwnedValue::Float(0.0), OwnedValue::Integer(0)), + ( + OwnedValue::Integer(0), + OwnedValue::Text(LimboText::new(Rc::new("string".to_string()))), + ), + ( + OwnedValue::Integer(0), + OwnedValue::Text(LimboText::new(Rc::new("1".to_string()))), + ), + ( + OwnedValue::Integer(0), + OwnedValue::Text(LimboText::new(Rc::new("".to_string()))), + ), + ]; + let outpus = vec![ + OwnedValue::Null, + OwnedValue::Integer(1), + OwnedValue::Null, + OwnedValue::Null, + OwnedValue::Integer(1), + OwnedValue::Integer(0), + OwnedValue::Integer(0), + OwnedValue::Integer(1), + OwnedValue::Integer(0), + ]; + + assert_eq!( + inputs.len(), + outpus.len(), + "Inputs and Outputs should have same size" + ); + for (i, (lhs, rhs)) in inputs.iter().enumerate() { + assert_eq!( + exec_or(lhs, rhs), + outpus[i], + "Wrong OR for lhs: {}, rhs: {}", + lhs, + rhs + ); + } + } } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 24eb7657c..2c4dd89fb 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -47,7 +47,8 @@ use crate::{resolve_ext_path, Connection, Result, Rows, TransactionState, DATABA use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_unixepoch}; use insn::{ exec_add, exec_and, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_concat, - exec_divide, exec_multiply, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract, + exec_divide, exec_multiply, exec_or, exec_remainder, exec_shift_left, exec_shift_right, + exec_subtract, }; use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape}; use rand::distributions::{Distribution, Uniform}; @@ -2351,6 +2352,11 @@ impl Program { exec_and(&state.registers[*lhs], &state.registers[*rhs]); state.pc += 1; } + Insn::Or { lhs, rhs, dest } => { + state.registers[*dest] = + exec_or(&state.registers[*lhs], &state.registers[*rhs]); + state.pc += 1; + } } } }