implementation of scalar functions min and max

This commit is contained in:
Brayan Jules 2024-07-21 15:13:22 -04:00
parent 62c480f6fb
commit dde00c3bc5
4 changed files with 155 additions and 2 deletions

View file

@ -38,6 +38,8 @@ pub enum SingleRowFunc {
Trim,
Round,
Length,
Min,
Max,
}
impl ToString for SingleRowFunc {
@ -52,6 +54,8 @@ impl ToString for SingleRowFunc {
SingleRowFunc::Trim => "trim".to_string(),
SingleRowFunc::Round => "round".to_string(),
SingleRowFunc::Length => "length".to_string(),
SingleRowFunc::Min => "min_arr".to_string(),
SingleRowFunc::Max => "max_arr".to_string(),
}
}
}
@ -84,6 +88,8 @@ impl FromStr for Func {
"trim" => Ok(Func::SingleRow(SingleRowFunc::Trim)),
"round" => Ok(Func::SingleRow(SingleRowFunc::Round)),
"length" => Ok(Func::SingleRow(SingleRowFunc::Length)),
"min_arr" => Ok(Func::SingleRow(SingleRowFunc::Min)),
"max_arr" => Ok(Func::SingleRow(SingleRowFunc::Max)),
_ => Err(()),
}
}

View file

@ -377,6 +377,60 @@ pub fn translate_expr(
});
Ok(target_register)
}
SingleRowFunc::Min => {
let args = if let Some(args) = args {
if args.len() < 1 {
anyhow::bail!(
"Parse error: min function with less than one argument"
);
}
args
} else {
anyhow::bail!("Parse error: min function with no arguments");
};
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::Min,
});
Ok(target_register)
}
SingleRowFunc::Max => {
let args = if let Some(args) = args {
if args.len() < 1 {
anyhow::bail!(
"Parse error: max function with less than one argument"
);
}
args
} else {
anyhow::bail!("Parse error: max function with no arguments");
};
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::Max,
});
Ok(target_register)
}
}
}
None => {

View file

@ -1362,6 +1362,32 @@ impl Program {
state.registers[*dest] = result;
state.pc += 1;
}
SingleRowFunc::Min => {
let start_reg = *start_reg;
let reg_values = state.registers[start_reg..state.registers.len()]
.iter()
.collect();
let min_fn = |a, b| if a < b { a } else { b };
if let Some(value) = exec_minmax(reg_values, min_fn) {
state.registers[*dest] = value;
} else {
state.registers[*dest] = OwnedValue::Null;
}
state.pc += 1;
}
SingleRowFunc::Max => {
let start_reg = *start_reg;
let reg_values = state.registers[start_reg..state.registers.len()]
.iter()
.collect();
let max_fn = |a, b| if a > b { a } else { b };
if let Some(value) = exec_minmax(reg_values, max_fn) {
state.registers[*dest] = value;
} else {
state.registers[*dest] = OwnedValue::Null;
}
state.pc += 1;
}
},
}
}
@ -1990,6 +2016,13 @@ fn exec_like(pattern: &str, text: &str) -> bool {
re.is_match(text)
}
fn exec_minmax<'a>(
regs: Vec<&'a OwnedValue>,
op: fn(&'a OwnedValue, &'a OwnedValue) -> &'a OwnedValue,
) -> Option<OwnedValue> {
regs.into_iter().reduce(|a, b| op(a, b)).cloned()
}
fn exec_round(reg: &OwnedValue, precision: Option<OwnedValue>) -> OwnedValue {
let precision = match precision {
Some(OwnedValue::Text(x)) => x.parse().unwrap_or(0.0),
@ -2045,7 +2078,7 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
#[cfg(test)]
mod tests {
use super::{
exec_abs, exec_if, exec_length, exec_like, exec_lower, exec_random, exec_round, exec_trim,
exec_abs, exec_if, exec_length, exec_like, exec_lower, exec_minmax, exec_random, exec_round, exec_trim,
exec_upper, OwnedValue,
};
use std::rc::Rc;
@ -2070,6 +2103,43 @@ mod tests {
assert_eq!(exec_length(&expected_blob), expected_len);
}
#[test]
fn test_minmax() {
let min_fn = |a, b| if a < b { a } else { b };
let max_fn = |a, b| if a > b { a } else { b };
let input_int_vec = vec![&OwnedValue::Integer(-1), &OwnedValue::Integer(10)];
assert_eq!(
exec_minmax(input_int_vec.clone(), min_fn),
Some(OwnedValue::Integer(-1))
);
assert_eq!(
exec_minmax(input_int_vec.clone(), max_fn),
Some(OwnedValue::Integer(10))
);
let str1 = OwnedValue::Text(Rc::new(String::from("A")));
let str2 = OwnedValue::Text(Rc::new(String::from("z")));
let input_str_vec = vec![&str2, &str1];
assert_eq!(
exec_minmax(input_str_vec.clone(), min_fn),
Some(OwnedValue::Text(Rc::new(String::from("A"))))
);
assert_eq!(
exec_minmax(input_str_vec.clone(), max_fn),
Some(OwnedValue::Text(Rc::new(String::from("z"))))
);
let input_null_vec = vec![&OwnedValue::Null, &OwnedValue::Null];
assert_eq!(
exec_minmax(input_null_vec.clone(), min_fn),
Some(OwnedValue::Null)
);
assert_eq!(
exec_minmax(input_null_vec.clone(), max_fn),
Some(OwnedValue::Null)
);
}
#[test]
fn test_trim() {
let input_str = OwnedValue::Text(Rc::new(String::from(" Bob and Alice ")));

View file

@ -141,4 +141,27 @@ do_execsql_test length-null {
do_execsql_test length-empty-text {
SELECT length('');
} {0}
} {0}
do_execsql_test min-number {
select min_arr(-10,2,3)
} {-10}
do_execsql_test min-str {
select min_arr('b','a','z')
} {a}
do_execsql_test min-null {
select min_arr(null,null)
} {}
do_execsql_test max-number {
select max_arr(-10,2,3)
} {3}
do_execsql_test max-str {
select max_arr('b','a','z')
} {z}
do_execsql_test max-null {
select max_arr(null,null)
} {}