From 6f983702c3b84ca7e39df2e961fb443f5557a87f Mon Sep 17 00:00:00 2001 From: Bennett Clement Date: Tue, 16 Jul 2024 16:33:57 +0800 Subject: [PATCH] Refactor if/ifnot implementation --- core/translate.rs | 3 ++ core/vdbe.rs | 127 +++++++++++++++++++++++++++++++++++----------- testing/like.test | 14 +++-- 3 files changed, 109 insertions(+), 35 deletions(-) diff --git a/core/translate.rs b/core/translate.rs index bc58d6299..1753249c6 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -749,6 +749,7 @@ fn translate_condition_expr( program.emit_insn(Insn::IfNot { reg, target_pc: target_jump, + null_reg: reg, }); } else { anyhow::bail!("Parse error: unsupported literal type in condition"); @@ -792,6 +793,7 @@ fn translate_condition_expr( Insn::If { reg: cur_reg, target_pc: target_jump, + null_reg: cur_reg, }, target_jump, ) @@ -800,6 +802,7 @@ fn translate_condition_expr( Insn::IfNot { reg: cur_reg, target_pc: target_jump, + null_reg: cur_reg, }, target_jump, ) diff --git a/core/vdbe.rs b/core/vdbe.rs index f87ec6537..853473fc9 100644 --- a/core/vdbe.rs +++ b/core/vdbe.rs @@ -74,15 +74,19 @@ pub enum Insn { rhs: usize, target_pc: BranchOffset, }, - // Jump to target_pc if r[reg] is not zero + /// Jump to target_pc if r\[reg\] != 0 or (r\[reg\] == NULL && r\[null_reg\] != 0) If { - reg: usize, - target_pc: BranchOffset, + reg: usize, // P1 + target_pc: BranchOffset, // P2 + /// P3. If r\[reg\] is null, jump iff r\[null_reg\] != 0 + null_reg: usize, }, - // Jump to the given PC if the register is zero. + /// Jump to target_pc if r\[reg\] != 0 or (r\[reg\] == NULL && r\[null_reg\] != 0) IfNot { - reg: usize, - target_pc: BranchOffset, + reg: usize, // P1 + target_pc: BranchOffset, // P2 + /// P3. If r\[reg\] is null, jump iff r\[null_reg\] != 0 + null_reg: usize, }, // Open a cursor for reading. OpenReadAsync { @@ -417,6 +421,7 @@ impl ProgramBuilder { Insn::If { reg: _reg, target_pc, + null_reg, } => { assert!(*target_pc < 0); *target_pc = to_offset; @@ -424,6 +429,7 @@ impl ProgramBuilder { Insn::IfNot { reg: _reg, target_pc, + null_reg, } => { assert!(*target_pc < 0); *target_pc = to_offset; @@ -784,30 +790,28 @@ impl Program { } } } - Insn::If { reg, target_pc } => { + Insn::If { + reg, + target_pc, + null_reg, + } => { assert!(*target_pc >= 0); - let reg = *reg; - let target_pc = *target_pc; - match &state.registers[reg] { - OwnedValue::Integer(0) => { - state.pc += 1; - } - _ => { - state.pc = target_pc; - } + if jump_if(&state.registers[*reg], &state.registers[*null_reg], false) { + state.pc = *target_pc; + } else { + state.pc += 1; } } - Insn::IfNot { reg, target_pc } => { + Insn::IfNot { + reg, + target_pc, + null_reg, + } => { assert!(*target_pc >= 0); - let reg = *reg; - let target_pc = *target_pc; - match &state.registers[reg] { - OwnedValue::Integer(0) => { - state.pc = target_pc; - } - _ => { - state.pc += 1; - } + if jump_if(&state.registers[*reg], &state.registers[*null_reg], true) { + state.pc = *target_pc; + } else { + state.pc += 1; } } Insn::OpenReadAsync { @@ -1398,20 +1402,28 @@ fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: Stri 0, format!("if r[{}]>=r[{}] goto {}", lhs, rhs, target_pc), ), - Insn::If { reg, target_pc } => ( + Insn::If { + reg, + target_pc, + null_reg, + } => ( "If", *reg as i32, *target_pc as i32, - 0, + *null_reg as i32, OwnedValue::Text(Rc::new("".to_string())), 0, format!("if r[{}] goto {}", reg, target_pc), ), - Insn::IfNot { reg, target_pc } => ( + Insn::IfNot { + reg, + target_pc, + null_reg, + } => ( "IfNot", *reg as i32, *target_pc as i32, - 0, + *null_reg as i32, OwnedValue::Text(Rc::new("".to_string())), 0, format!("if !r[{}] goto {}", reg, target_pc), @@ -1757,3 +1769,58 @@ fn like(pattern: &str, text: &str) -> bool { let re = Regex::new(&format!("{}", pattern.replace("%", ".*").replace("_", "."))).unwrap(); re.is_match(text) } + +// step_if returns whether you should jump +fn jump_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool { + match reg { + OwnedValue::Integer(0) | OwnedValue::Float(0.0) => not, + OwnedValue::Integer(_) | OwnedValue::Float(_) => !not, + OwnedValue::Null => match null_reg { + OwnedValue::Integer(0) | OwnedValue::Float(0.0) => false, + OwnedValue::Integer(_) | OwnedValue::Float(_) => true, + _ => false, + }, + _ => false, + } +} + +mod tests { + use super::*; + + #[test] + fn test_like() { + assert!(like("a%", "aaaa")); + assert!(like("%a%a", "aaaa")); + assert!(like("%a.a", "aaaa")); + assert!(like("a.a%", "aaaa")); + assert!(!like("%a.ab", "aaaa")); + } + + #[test] + fn test_jump_if() { + let reg = OwnedValue::Integer(0); + let null_reg = OwnedValue::Integer(0); + assert_eq!(jump_if(®, &null_reg, false), false); + assert_eq!(jump_if(®, &null_reg, true), true); + + let reg = OwnedValue::Integer(1); + let null_reg = OwnedValue::Integer(0); + assert_eq!(jump_if(®, &null_reg, false), true); + assert_eq!(jump_if(®, &null_reg, true), false); + + let reg = OwnedValue::Null; + let null_reg = OwnedValue::Integer(0); + assert_eq!(jump_if(®, &null_reg, false), false); + assert_eq!(jump_if(®, &null_reg, true), false); + + let reg = OwnedValue::Null; + let null_reg = OwnedValue::Integer(1); + assert_eq!(jump_if(®, &null_reg, false), true); + assert_eq!(jump_if(®, &null_reg, true), true); + + let reg = OwnedValue::Null; + let null_reg = OwnedValue::Null; + assert_eq!(jump_if(®, &null_reg, false), false); + assert_eq!(jump_if(®, &null_reg, true), false); + } +} diff --git a/testing/like.test b/testing/like.test index 799062789..812bcb67a 100644 --- a/testing/like.test +++ b/testing/like.test @@ -19,10 +19,14 @@ accessories|0} do_execsql_test where-like { select * from products where name like 'sweat%'; -} {4|sweater|2.0 -5|sweatshirt|67.0} +} {4|sweater|25.0 +5|sweatshirt|74.0} do_execsql_test where-not-like { - select * from products where name not like 'sneak%' and price >= 70.0; -} {3|shirt|79.0 -7|jeans|80.0} + select * from products where name not like 'sweat%' and price >= 70.0; +} {1|hat|79.0 +2|cap|82.0 +6|shorts|70.0 +7|jeans|78.0 +8|sneakers|82.0 +11|accessories|81.0}