mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-27 04:54:21 +01:00
Merge pull request #186 from JeanArhancet/feat/add-round-scalar-function
This commit is contained in:
@@ -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 | |
|
||||
|
||||
37
core/expr.rs
37
core/expr.rs
@@ -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 => {
|
||||
|
||||
@@ -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(()),
|
||||
}
|
||||
}
|
||||
|
||||
59
core/vdbe.rs
59
core/vdbe.rs
@@ -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(®_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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
} {}
|
||||
|
||||
Reference in New Issue
Block a user