Support log(X) and log(B,X) math functions

This commit is contained in:
Lauri Virtanen
2024-12-15 22:30:04 +02:00
parent 89d0289444
commit e69ee80fac
4 changed files with 140 additions and 4 deletions

View File

@@ -179,8 +179,8 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html).
| exp(X) | Yes | |
| floor(X) | Yes | |
| ln(X) | Yes | |
| log(B,X) | No | |
| log(X) | No | |
| log(B,X) | Yes | |
| log(X) | Yes | |
| log10(X) | Yes | |
| log2(X) | Yes | |
| mod(X,Y) | Yes | |

View File

@@ -1696,7 +1696,40 @@ pub fn translate_expr(
});
Ok(target_register)
}
_ => unimplemented!(),
MathFuncArity::UnaryOrBinary => {
let args = if let Some(args) = args {
if args.len() > 2 {
crate::bail_parse_error!(
"{} function with more than 2 arguments",
math_func
);
}
args
} else {
crate::bail_parse_error!("{} function with no arguments", math_func);
};
let regs = program.alloc_registers(args.len());
for (i, arg) in args.iter().enumerate() {
translate_expr(
program,
referenced_tables,
arg,
regs + i,
precomputed_exprs_to_registers,
)?;
}
program.emit_insn(Insn::Function {
constant_mask: 0,
start_reg: regs,
dest: target_register,
func: func_ctx,
});
Ok(target_register)
}
},
}
}

View File

@@ -2509,7 +2509,24 @@ impl Program {
state.registers[*dest] = result;
}
_ => unimplemented!(),
MathFuncArity::UnaryOrBinary => match math_func {
MathFunc::Log => {
let lhs = &state.registers[*start_reg];
let rhs = state.registers.get(*start_reg + 1);
let result = if let Some(arg) = rhs {
exec_math_log(arg, Some(lhs))
} else {
exec_math_log(lhs, None)
};
state.registers[*dest] = result;
}
_ => unreachable!(
"Unexpected mathematical UnaryOrBinary function {:?}",
math_func
),
},
},
crate::function::Func::Agg(_) => {
unreachable!("Aggregate functions should not be handled here")
@@ -3699,6 +3716,27 @@ fn exec_math_binary(lhs: &OwnedValue, rhs: &OwnedValue, function: &MathFunc) ->
}
}
fn exec_math_log(arg: &OwnedValue, base: Option<&OwnedValue>) -> OwnedValue {
let f = match to_f64(arg) {
Some(f) => f,
None => return OwnedValue::Null,
};
let base = match base {
Some(base) => match to_f64(base) {
Some(f) => f,
None => return OwnedValue::Null,
},
None => 10.0,
};
if f <= 0.0 || base <= 0.0 || base == 1.0 {
return OwnedValue::Null;
}
OwnedValue::Float(f.log(base))
}
#[cfg(test)]
mod tests {

View File

@@ -976,3 +976,68 @@ do_execsql_test power-null-int {
do_execsql_test power-int-null {
SELECT power(5, null)
} {}
do_execsql_test_tolerance log-int {
SELECT log(1)
} {0.0} $tolerance
do_execsql_test_tolerance log-float {
SELECT log(1.5)
} {0.176091259055681} $tolerance
do_execsql_test_tolerance log-str {
SELECT log('1.5')
} {0.176091259055681} $tolerance
do_execsql_test log-negative {
SELECT log(-1.5)
} {}
do_execsql_test log-null {
SELECT log(null)
} {}
do_execsql_test_tolerance log-int-int {
SELECT log(5, 1)
} {0.0} $tolerance
do_execsql_test_tolerance log-int-float {
SELECT log(5, 1.5)
} {0.251929636412592} $tolerance
do_execsql_test_tolerance log-int-str {
SELECT log(5, '1.5')
} {0.251929636412592} $tolerance
do_execsql_test_tolerance log-float-int {
SELECT log(5.5, 10)
} {1.35068935021985} $tolerance
do_execsql_test_tolerance log-float-float {
SELECT log(5.5, 1.5)
} {0.237844588273313} $tolerance
do_execsql_test_tolerance log-float-str {
SELECT log(5.5, '1.5')
} {0.237844588273313} $tolerance
do_execsql_test_tolerance log-str-str {
SELECT log('5.5', '1.5')
} {0.237844588273313} $tolerance
do_execsql_test log-negative-negative {
SELECT log(-1.5, -1.5)
} {}
do_execsql_test log-float-negative {
SELECT log(1.5, -1.5)
} {}
do_execsql_test log-null-int {
SELECT log(null, 5)
} {}
do_execsql_test log-int-null {
SELECT log(5, null)
} {}