diff --git a/COMPAT.md b/COMPAT.md index 0bacfc0e3..913b0366d 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -111,7 +111,7 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html). | glob(X,Y) | Yes | | | hex(X) | Yes | | | ifnull(X,Y) | Yes | | -| iif(X,Y,Z) | No | | +| iif(X,Y,Z) | Yes | | | instr(X,Y) | Yes | | | last_insert_rowid() | Yes | | | length(X) | Yes | | diff --git a/core/function.rs b/core/function.rs index 33ea43eb1..9ece12992 100644 --- a/core/function.rs +++ b/core/function.rs @@ -56,6 +56,7 @@ pub enum ScalarFunc { ConcatWs, Glob, IfNull, + Iif, Instr, Like, Abs, @@ -98,6 +99,7 @@ impl Display for ScalarFunc { ScalarFunc::ConcatWs => "concat_ws".to_string(), ScalarFunc::Glob => "glob".to_string(), ScalarFunc::IfNull => "ifnull".to_string(), + ScalarFunc::Iif => "iif".to_string(), ScalarFunc::Instr => "instr".to_string(), ScalarFunc::Like => "like(2)".to_string(), ScalarFunc::Abs => "abs".to_string(), @@ -178,6 +180,7 @@ impl Func { "concat_ws" => Ok(Func::Scalar(ScalarFunc::ConcatWs)), "glob" => Ok(Func::Scalar(ScalarFunc::Glob)), "ifnull" => Ok(Func::Scalar(ScalarFunc::IfNull)), + "iif" => Ok(Func::Scalar(ScalarFunc::Iif)), "instr" => Ok(Func::Scalar(ScalarFunc::Instr)), "like" => Ok(Func::Scalar(ScalarFunc::Like)), "abs" => Ok(Func::Scalar(ScalarFunc::Abs)), diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 0d84815a5..8a13e1344 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -1071,6 +1071,56 @@ pub fn translate_expr( Ok(target_register) } + ScalarFunc::Iif => { + let args = match args { + Some(args) if args.len() == 3 => args, + _ => crate::bail_parse_error!( + "{} requires exactly 3 arguments", + srf.to_string() + ), + }; + let temp_reg = program.alloc_register(); + translate_expr( + program, + referenced_tables, + &args[0], + temp_reg, + precomputed_exprs_to_registers, + )?; + let jump_target_when_false = program.allocate_label(); + program.emit_insn_with_label_dependency( + Insn::IfNot { + reg: temp_reg, + target_pc: jump_target_when_false, + null_reg: 1, + }, + jump_target_when_false, + ); + translate_expr( + program, + referenced_tables, + &args[1], + target_register, + precomputed_exprs_to_registers, + )?; + let jump_target_result = program.allocate_label(); + program.emit_insn_with_label_dependency( + Insn::Goto { + target_pc: jump_target_result, + }, + jump_target_result, + ); + program.resolve_label(jump_target_when_false, program.offset()); + translate_expr( + program, + referenced_tables, + &args[2], + target_register, + precomputed_exprs_to_registers, + )?; + program.resolve_label(jump_target_result, program.offset()); + Ok(target_register) + } ScalarFunc::Glob | ScalarFunc::Like => { let args = if let Some(args) = args { if args.len() < 2 { diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index ca6309a11..ebd25360a 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -2106,6 +2106,13 @@ impl Program { state.registers[*dest] = result; } ScalarFunc::IfNull => {} + ScalarFunc::Iif => {} + ScalarFunc::Instr => { + let reg_value = &state.registers[*start_reg]; + let pattern_value = &state.registers[*start_reg + 1]; + let result = exec_instr(reg_value, pattern_value); + state.registers[*dest] = result; + } ScalarFunc::LastInsertRowid => { if let Some(conn) = self.connection.upgrade() { state.registers[*dest] = @@ -2114,12 +2121,6 @@ impl Program { state.registers[*dest] = OwnedValue::Null; } } - ScalarFunc::Instr => { - let reg_value = &state.registers[*start_reg]; - let pattern_value = &state.registers[*start_reg + 1]; - let result = exec_instr(reg_value, pattern_value); - state.registers[*dest] = result; - } ScalarFunc::Like => { let pattern = &state.registers[*start_reg]; let text = &state.registers[*start_reg + 1]; diff --git a/testing/scalar-functions.test b/testing/scalar-functions.test index e0ff8c9f1..ae96ddad8 100755 --- a/testing/scalar-functions.test +++ b/testing/scalar-functions.test @@ -75,6 +75,14 @@ do_execsql_test ifnull-2 { select ifnull(null, 2); } {2} +do_execsql_test iif-true { + select iif(1, 'pass', 'fail'); +} {pass} + +do_execsql_test iif-false { + select iif(0, 'fail', 'pass'); +} {pass} + do_execsql_test instr-str { select instr('limbo', 'im'); } {2}