Skip to content

Commit d696a67

Browse files
committed
feat: add round scalar function
1 parent 4b7e86c commit d696a67

File tree

6 files changed

+138
-4
lines changed

6 files changed

+138
-4
lines changed

COMPAT.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ This document describes the SQLite compatibility status of Limbo:
9393
| random() | Yes | |
9494
| randomblob(N) | No | |
9595
| replace(X,Y,Z) | No | |
96-
| round(X) | No | |
97-
| round(X,Y) | No | |
96+
| round(X) | Yes | |
97+
| round(X,Y) | Yes | |
9898
| rtrim(X) | No | |
9999
| rtrim(X,Y) | No | |
100100
| sign(X) | No | |

core/expr.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,43 @@ pub fn translate_expr(
405405
}
406406
Ok(target_register)
407407
}
408+
SingleRowFunc::Round => {
409+
let args = if let Some(args) = args {
410+
if args.len() > 2 {
411+
anyhow::bail!(
412+
"Parse error: round function with more than 2 arguments"
413+
);
414+
}
415+
args
416+
} else {
417+
anyhow::bail!("Parse error: round function with no arguments");
418+
};
419+
420+
if args.len() == 1 {
421+
let regs = program.alloc_register();
422+
let _ = translate_expr(program, select, &args[0], regs)?;
423+
program.emit_insn(Insn::Function {
424+
start_reg: regs,
425+
dest: target_register,
426+
func: SingleRowFunc::Round,
427+
});
428+
} else {
429+
for arg in args {
430+
let reg = program.alloc_register();
431+
let _ = translate_expr(program, select, arg, reg)?;
432+
match arg {
433+
ast::Expr::Literal(_) => program.mark_last_insn_constant(),
434+
_ => {}
435+
}
436+
}
437+
program.emit_insn(Insn::Function {
438+
start_reg: target_register + 1,
439+
dest: target_register,
440+
func: SingleRowFunc::Round,
441+
});
442+
}
443+
Ok(target_register)
444+
}
408445
}
409446
}
410447
None => {

core/function.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum SingleRowFunc {
3636
Lower,
3737
Random,
3838
Trim,
39+
Round,
3940
}
4041

4142
impl ToString for SingleRowFunc {
@@ -48,6 +49,7 @@ impl ToString for SingleRowFunc {
4849
SingleRowFunc::Lower => "lower".to_string(),
4950
SingleRowFunc::Random => "random".to_string(),
5051
SingleRowFunc::Trim => "trim".to_string(),
52+
SingleRowFunc::Round => "round".to_string(),
5153
}
5254
}
5355
}
@@ -77,6 +79,7 @@ impl FromStr for Func {
7779
"lower" => Ok(Func::SingleRow(SingleRowFunc::Lower)),
7880
"random" => Ok(Func::SingleRow(SingleRowFunc::Random)),
7981
"trim" => Ok(Func::SingleRow(SingleRowFunc::Trim)),
82+
"round" => Ok(Func::SingleRow(SingleRowFunc::Round)),
8083
_ => Err(()),
8184
}
8285
}

core/vdbe.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,14 @@ impl Program {
13031303
state.registers[*dest] = result;
13041304
state.pc += 1;
13051305
}
1306+
SingleRowFunc::Round => {
1307+
let start_reg = *start_reg;
1308+
let reg_value = state.registers[start_reg].clone();
1309+
let precision_value = state.registers.get(start_reg + 1).cloned();
1310+
let result = exec_round(&reg_value, precision_value);
1311+
state.registers[*dest] = result;
1312+
state.pc += 1;
1313+
}
13061314
},
13071315
}
13081316
}
@@ -1888,6 +1896,27 @@ fn exec_like(pattern: &str, text: &str) -> bool {
18881896
re.is_match(text)
18891897
}
18901898

1899+
fn exec_round(reg: &OwnedValue, precision: Option<OwnedValue>) -> OwnedValue {
1900+
let precision = match precision {
1901+
Some(OwnedValue::Text(x)) => x.parse().unwrap_or(0.0),
1902+
Some(OwnedValue::Integer(x)) => x as f64,
1903+
Some(OwnedValue::Float(x)) => x,
1904+
None => 0.0,
1905+
_ => return OwnedValue::Null,
1906+
};
1907+
1908+
let reg = match reg {
1909+
OwnedValue::Text(x) => x.parse().unwrap_or(0.0),
1910+
OwnedValue::Integer(x) => *x as f64,
1911+
OwnedValue::Float(x) => *x,
1912+
_ => return reg.to_owned(),
1913+
};
1914+
1915+
let precision = if precision < 1.0 { 0.0 } else { precision };
1916+
let multiplier = 10f64.powi(precision as i32);
1917+
OwnedValue::Float(((reg * multiplier).round()) / multiplier)
1918+
}
1919+
18911920
// Implements TRIM pattern matching.
18921921
fn exec_trim(reg: &OwnedValue, pattern: Option<OwnedValue>) -> OwnedValue {
18931922
match (reg, pattern) {
@@ -1922,7 +1951,8 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
19221951
#[cfg(test)]
19231952
mod tests {
19241953
use super::{
1925-
exec_abs, exec_if, exec_like, exec_lower, exec_random, exec_trim, exec_upper, OwnedValue,
1954+
exec_abs, exec_if, exec_like, exec_lower, exec_random, exec_round, exec_trim, exec_upper,
1955+
OwnedValue,
19261956
};
19271957
use std::rc::Rc;
19281958

@@ -2001,6 +2031,33 @@ mod tests {
20012031
}
20022032
}
20032033

2034+
#[test]
2035+
fn test_exec_round() {
2036+
let input_val = OwnedValue::Float(123.456);
2037+
let expected_val = OwnedValue::Float(123.0);
2038+
assert_eq!(exec_round(&input_val, None), expected_val);
2039+
2040+
let input_val = OwnedValue::Float(123.456);
2041+
let precision_val = OwnedValue::Integer(2);
2042+
let expected_val = OwnedValue::Float(123.46);
2043+
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
2044+
2045+
let input_val = OwnedValue::Float(123.456);
2046+
let precision_val = OwnedValue::Text(Rc::new(String::from("1")));
2047+
let expected_val = OwnedValue::Float(123.5);
2048+
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
2049+
2050+
let input_val = OwnedValue::Text(Rc::new(String::from("123.456")));
2051+
let precision_val = OwnedValue::Integer(2);
2052+
let expected_val = OwnedValue::Float(123.46);
2053+
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
2054+
2055+
let input_val = OwnedValue::Integer(123);
2056+
let precision_val = OwnedValue::Integer(1);
2057+
let expected_val = OwnedValue::Float(123.0);
2058+
assert_eq!(exec_round(&input_val, Some(precision_val)), expected_val);
2059+
}
2060+
20042061
#[test]
20052062
fn test_exec_if() {
20062063
let reg = OwnedValue::Integer(0);

sqlite3/tests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ PROGRAM = sqlite3-tests
1313
CFLAGS = -g -Wall -std=c17 -MMD -MP
1414

1515
LIBS ?= -lsqlite3
16+
LIBS += -lm
1617

1718
OBJS += main.o
1819
OBJS += test-aux.o

testing/scalar-functions.test

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,40 @@ do_execsql_test trim-pattern-null {
8585

8686
do_execsql_test trim-no-match-pattern {
8787
SELECT trim('Limbo', 'xyz');
88-
} {Limbo}
88+
} {Limbo}
89+
90+
do_execsql_test round-float-no-precision {
91+
SELECT round(123.456);
92+
} {123.0}
93+
94+
do_execsql_test round-float-with-precision {
95+
SELECT round(123.456, 2);
96+
} {123.46}
97+
98+
do_execsql_test round-float-with-text-precision {
99+
SELECT round(123.5, '1');
100+
} {123.5}
101+
102+
do_execsql_test round-text-parsable {
103+
SELECT round('123.456', 2);
104+
} {123.46}
105+
106+
do_execsql_test round-text-non-parsable {
107+
SELECT round('abc', 1);
108+
} {0.0}
109+
110+
do_execsql_test round-integer-with-precision {
111+
SELECT round(123, 1);
112+
} {123.0}
113+
114+
do_execsql_test round-float-negative-precision {
115+
SELECT round(123.456, -1);
116+
} {123.0}
117+
118+
do_execsql_test round-float-zero-precision {
119+
SELECT round(123.456, 0);
120+
} {123.0}
121+
122+
do_execsql_test round-null-precision {
123+
SELECT round(123.456, null);
124+
} {}

0 commit comments

Comments
 (0)