Merge pull request #186 from JeanArhancet/feat/add-round-scalar-function

This commit is contained in:
Pekka Enberg
2024-07-21 12:55:04 +03:00
committed by GitHub
6 changed files with 138 additions and 4 deletions

View File

@@ -93,8 +93,8 @@ This document describes the SQLite compatibility status of Limbo:
| random() | Yes | |
| randomblob(N) | No | |
| replace(X,Y,Z) | No | |
| round(X) | No | |
| round(X,Y) | No | |
| round(X) | Yes | |
| round(X,Y) | Yes | |
| rtrim(X) | No | |
| rtrim(X,Y) | No | |
| sign(X) | No | |

View File

@@ -405,6 +405,43 @@ pub fn translate_expr(
}
Ok(target_register)
}
SingleRowFunc::Round => {
let args = if let Some(args) = args {
if args.len() > 2 {
anyhow::bail!(
"Parse error: round function with more than 2 arguments"
);
}
args
} else {
anyhow::bail!("Parse error: round function with no arguments");
};
if args.len() == 1 {
let regs = program.alloc_register();
let _ = translate_expr(program, select, &args[0], regs)?;
program.emit_insn(Insn::Function {
start_reg: regs,
dest: target_register,
func: SingleRowFunc::Round,
});
} else {
for arg in args {
let reg = program.alloc_register();
let _ = translate_expr(program, select, arg, reg)?;
match arg {
ast::Expr::Literal(_) => program.mark_last_insn_constant(),
_ => {}
}
}
program.emit_insn(Insn::Function {
start_reg: target_register + 1,
dest: target_register,
func: SingleRowFunc::Round,
});
}
Ok(target_register)
}
}
}
None => {

View File

@@ -36,6 +36,7 @@ pub enum SingleRowFunc {
Lower,
Random,
Trim,
Round,
}
impl ToString for SingleRowFunc {
@@ -48,6 +49,7 @@ impl ToString for SingleRowFunc {
SingleRowFunc::Lower => "lower".to_string(),
SingleRowFunc::Random => "random".to_string(),
SingleRowFunc::Trim => "trim".to_string(),
SingleRowFunc::Round => "round".to_string(),
}
}
}
@@ -77,6 +79,7 @@ impl FromStr for Func {
"lower" => Ok(Func::SingleRow(SingleRowFunc::Lower)),
"random" => Ok(Func::SingleRow(SingleRowFunc::Random)),
"trim" => Ok(Func::SingleRow(SingleRowFunc::Trim)),
"round" => Ok(Func::SingleRow(SingleRowFunc::Round)),
_ => Err(()),
}
}

View File

@@ -1303,6 +1303,14 @@ impl Program {
state.registers[*dest] = result;
state.pc += 1;
}
SingleRowFunc::Round => {
let start_reg = *start_reg;
let reg_value = state.registers[start_reg].clone();
let precision_value = state.registers.get(start_reg + 1).cloned();
let result = exec_round(&reg_value, precision_value);
state.registers[*dest] = result;
state.pc += 1;
}
},
}
}
@@ -1888,6 +1896,27 @@ fn exec_like(pattern: &str, text: &str) -> bool {
re.is_match(text)
}
fn exec_round(reg: &OwnedValue, precision: Option<OwnedValue>) -> OwnedValue {
let precision = match precision {
Some(OwnedValue::Text(x)) => x.parse().unwrap_or(0.0),
Some(OwnedValue::Integer(x)) => x as f64,
Some(OwnedValue::Float(x)) => x,
None => 0.0,
_ => return OwnedValue::Null,
};
let reg = match reg {
OwnedValue::Text(x) => x.parse().unwrap_or(0.0),
OwnedValue::Integer(x) => *x as f64,
OwnedValue::Float(x) => *x,
_ => return reg.to_owned(),
};
let precision = if precision < 1.0 { 0.0 } else { precision };
let multiplier = 10f64.powi(precision as i32);
OwnedValue::Float(((reg * multiplier).round()) / multiplier)
}
// Implements TRIM pattern matching.
fn exec_trim(reg: &OwnedValue, pattern: Option<OwnedValue>) -> OwnedValue {
match (reg, pattern) {
@@ -1922,7 +1951,8 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
#[cfg(test)]
mod tests {
use super::{
exec_abs, exec_if, exec_like, exec_lower, exec_random, exec_trim, exec_upper, OwnedValue,
exec_abs, exec_if, exec_like, exec_lower, exec_random, exec_round, exec_trim, exec_upper,
OwnedValue,
};
use std::rc::Rc;
@@ -2001,6 +2031,33 @@ mod tests {
}
}
#[test]
fn test_exec_round() {
let input_val = OwnedValue::Float(123.456);
let expected_val = OwnedValue::Float(123.0);
assert_eq!(exec_round(&input_val, None), expected_val);
let input_val = OwnedValue::Float(123.456);
let precision_val = OwnedValue::Integer(2);
let expected_val = OwnedValue::Float(123.46);
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
let input_val = OwnedValue::Float(123.456);
let precision_val = OwnedValue::Text(Rc::new(String::from("1")));
let expected_val = OwnedValue::Float(123.5);
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
let input_val = OwnedValue::Text(Rc::new(String::from("123.456")));
let precision_val = OwnedValue::Integer(2);
let expected_val = OwnedValue::Float(123.46);
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
let input_val = OwnedValue::Integer(123);
let precision_val = OwnedValue::Integer(1);
let expected_val = OwnedValue::Float(123.0);
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
}
#[test]
fn test_exec_if() {
let reg = OwnedValue::Integer(0);

View File

@@ -13,6 +13,7 @@ PROGRAM = sqlite3-tests
CFLAGS = -g -Wall -std=c17 -MMD -MP
LIBS ?= -lsqlite3
LIBS += -lm
OBJS += main.o
OBJS += test-aux.o

View File

@@ -85,4 +85,40 @@ do_execsql_test trim-pattern-null {
do_execsql_test trim-no-match-pattern {
SELECT trim('Limbo', 'xyz');
} {Limbo}
} {Limbo}
do_execsql_test round-float-no-precision {
SELECT round(123.456);
} {123.0}
do_execsql_test round-float-with-precision {
SELECT round(123.456, 2);
} {123.46}
do_execsql_test round-float-with-text-precision {
SELECT round(123.5, '1');
} {123.5}
do_execsql_test round-text-parsable {
SELECT round('123.456', 2);
} {123.46}
do_execsql_test round-text-non-parsable {
SELECT round('abc', 1);
} {0.0}
do_execsql_test round-integer-with-precision {
SELECT round(123, 1);
} {123.0}
do_execsql_test round-float-negative-precision {
SELECT round(123.456, -1);
} {123.0}
do_execsql_test round-float-zero-precision {
SELECT round(123.456, 0);
} {123.0}
do_execsql_test round-null-precision {
SELECT round(123.456, null);
} {}