From e7d95399e347dbe115cd43f346fcd6f2c02c200e Mon Sep 17 00:00:00 2001 From: Diego Reis Date: Sat, 25 Jan 2025 02:54:14 -0300 Subject: [PATCH] Add Or bytecode Take the logical OR of the values in register P1 and P2 and store the answer in register P3. If either P1 or P2 is nonzero (true) then the result is 1 (true) even if the other input is NULL. A NULL and false or two NULLs give a NULL output. --- COMPAT.md | 2 +- core/translate/expr.rs | 7 ++++ core/vdbe/explain.rs | 9 +++++ core/vdbe/insn.rs | 92 +++++++++++++++++++++++++++++++++++++++++- core/vdbe/mod.rs | 8 +++- 5 files changed, 115 insertions(+), 3 deletions(-) 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; + } } } }