Support binary math functions

This commit is contained in:
Lauri Virtanen
2024-11-26 00:27:39 +02:00
parent f69fdc1645
commit 5e426a7624
4 changed files with 263 additions and 4 deletions

View File

@@ -169,7 +169,7 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
| asin(X) | Yes | |
| asinh(X) | Yes | |
| atan(X) | Yes | |
| atan2(Y,X) | No | |
| atan2(Y,X) | Yes | |
| atanh(X) | Yes | |
| ceil(X) | Yes | |
| ceiling(X) | Yes | |
@@ -183,10 +183,10 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
| log(X) | No | |
| log10(X) | Yes | |
| log2(X) | Yes | |
| mod(X,Y) | No | |
| mod(X,Y) | Yes | |
| pi() | No | |
| pow(X,Y) | No | |
| power(X,Y) | No | |
| pow(X,Y) | Yes | |
| power(X,Y) | Yes | |
| radians(X) | Yes | |
| sin(X) | Yes | |
| sinh(X) | Yes | |

View File

@@ -1635,6 +1635,53 @@ pub fn translate_expr(
});
Ok(target_register)
}
MathFuncArity::Binary => {
let args = if let Some(args) = args {
if args.len() != 2 {
crate::bail_parse_error!(
"{} function with not exactly 2 arguments",
math_func
);
}
args
} else {
crate::bail_parse_error!("{} function with no arguments", math_func);
};
let reg1 = program.alloc_register();
let reg2 = program.alloc_register();
translate_expr(
program,
referenced_tables,
&args[0],
reg1,
precomputed_exprs_to_registers,
)?;
if let ast::Expr::Literal(_) = &args[0] {
program.mark_last_insn_constant();
}
translate_expr(
program,
referenced_tables,
&args[1],
reg2,
precomputed_exprs_to_registers,
)?;
if let ast::Expr::Literal(_) = &args[1] {
program.mark_last_insn_constant();
}
program.emit_insn(Insn::Function {
constant_mask: 0,
start_reg: target_register + 1,
dest: target_register,
func: func_ctx,
});
Ok(target_register)
}
_ => unimplemented!(),
},
}

View File

@@ -2497,6 +2497,13 @@ impl Program {
let result = exec_math_unary(reg_value, math_func);
state.registers[*dest] = result;
}
MathFuncArity::Binary => {
let lhs = &state.registers[*start_reg];
let rhs = &state.registers[*start_reg + 1];
let result = exec_math_binary(lhs, rhs, math_func);
state.registers[*dest] = result;
}
_ => unimplemented!(),
},
crate::function::Func::Agg(_) => {
@@ -3662,6 +3669,31 @@ fn exec_math_unary(reg: &OwnedValue, function: &MathFunc) -> OwnedValue {
}
}
fn exec_math_binary(lhs: &OwnedValue, rhs: &OwnedValue, function: &MathFunc) -> OwnedValue {
let lhs = match to_f64(lhs) {
Some(f) => f,
None => return OwnedValue::Null,
};
let rhs = match to_f64(rhs) {
Some(f) => f,
None => return OwnedValue::Null,
};
let result = match function {
MathFunc::Atan2 => lhs.atan2(rhs),
MathFunc::Mod => lhs % rhs,
MathFunc::Pow | MathFunc::Power => lhs.powf(rhs),
_ => unreachable!("Unexpected mathematical binary function {:?}", function),
};
if result.is_nan() {
OwnedValue::Null
} else {
OwnedValue::Float(result)
}
}
#[cfg(test)]
mod tests {

View File

@@ -791,3 +791,183 @@ do_execsql_test trunc-str {
do_execsql_test trunc-null {
SELECT trunc(null)
} {}
do_execsql_test_tolerance atan2-int-int {
SELECT atan2(5, -1)
} {1.76819188664478} $tolerance
do_execsql_test_tolerance atan2-int-float {
SELECT atan2(5, -1.5)
} {1.86225312127276} $tolerance
do_execsql_test_tolerance atan2-int-str {
SELECT atan2(5, '-1.5')
} {1.86225312127276} $tolerance
do_execsql_test_tolerance atan2-float-int {
SELECT atan2(5.5, 10)
} {0.502843210927861} $tolerance
do_execsql_test_tolerance atan2-float-float {
SELECT atan2(5.5, -1.5)
} {1.83704837594582} $tolerance
do_execsql_test_tolerance atan2-float-str {
SELECT atan2(5.5, '-1.5')
} {1.83704837594582} $tolerance
do_execsql_test_tolerance atan2-str-str {
SELECT atan2('5.5', '-1.5')
} {1.83704837594582} $tolerance
do_execsql_test atan2-null-int {
SELECT atan2(null, 5)
} {}
do_execsql_test atan2-int-null {
SELECT atan2(5, null)
} {}
do_execsql_test_tolerance mod-int-int {
SELECT mod(10, -3)
} {1.0} $tolerance
do_execsql_test_tolerance mod-int-float {
SELECT mod(5, -1.5)
} {0.5} $tolerance
do_execsql_test_tolerance mod-int-str {
SELECT mod(5, '-1.5')
} {0.5} $tolerance
do_execsql_test_tolerance mod-float-int {
SELECT mod(5.5, 2)
} {1.5} $tolerance
do_execsql_test_tolerance mod-float-float {
SELECT mod(5.5, -1.5)
} {1.0} $tolerance
do_execsql_test_tolerance mod-float-str {
SELECT mod(5.5, '-1.5')
} {1.0} $tolerance
do_execsql_test_tolerance mod-str-str {
SELECT mod('5.5', '-1.5')
} {1.0} $tolerance
do_execsql_test mod-null-int {
SELECT mod(null, 5)
} {}
do_execsql_test mod-int-null {
SELECT mod(5, null)
} {}
do_execsql_test mod-float-zero {
SELECT mod(1.5, 0)
} {}
do_execsql_test mod-products-id {
SELECT mod(products.id, 3) from products
} {1.0
2.0
0.0
1.0
2.0
0.0
1.0
2.0
0.0
1.0
2.0}
do_execsql_test mod-products-price-id {
SELECT mod(products.price, products.id) from products
} {0.0
0.0
0.0
1.0
4.0
4.0
1.0
2.0
1.0
3.0
4.0}
do_execsql_test_tolerance pow-int-int {
SELECT pow(5, -1)
} {0.2} $tolerance
do_execsql_test_tolerance pow-int-float {
SELECT pow(5, -1.5)
} {0.0894427190999916} $tolerance
do_execsql_test_tolerance pow-int-str {
SELECT pow(5, '-1.5')
} {0.0894427190999916} $tolerance
do_execsql_test_tolerance pow-float-int {
SELECT pow(5.5, 2)
} {30.25} $tolerance
do_execsql_test_tolerance pow-float-float {
SELECT pow(5.5, -1.5)
} {0.077527533220222} $tolerance
do_execsql_test_tolerance pow-float-str {
SELECT pow(5.5, '-1.5')
} {0.077527533220222} $tolerance
do_execsql_test_tolerance pow-str-str {
SELECT pow('5.5', '-1.5')
} {0.077527533220222} $tolerance
do_execsql_test pow-null-int {
SELECT pow(null, 5)
} {}
do_execsql_test pow-int-null {
SELECT pow(5, null)
} {}
do_execsql_test_tolerance power-int-int {
SELECT power(5, -1)
} {0.2} $tolerance
do_execsql_test_tolerance power-int-float {
SELECT power(5, -1.5)
} {0.0894427190999916} $tolerance
do_execsql_test_tolerance power-int-str {
SELECT power(5, '-1.5')
} {0.0894427190999916} $tolerance
do_execsql_test_tolerance power-float-int {
SELECT power(5.5, 2)
} {30.25} $tolerance
do_execsql_test_tolerance power-float-float {
SELECT power(5.5, -1.5)
} {0.077527533220222} $tolerance
do_execsql_test_tolerance power-float-str {
SELECT power(5.5, '-1.5')
} {0.077527533220222} $tolerance
do_execsql_test_tolerance power-str-str {
SELECT power('5.5', '-1.5')
} {0.077527533220222} $tolerance
do_execsql_test power-null-int {
SELECT power(null, 5)
} {}
do_execsql_test power-int-null {
SELECT power(5, null)
} {}