feat: add trim scalar function

This commit is contained in:
JeanArhancet
2024-07-20 11:39:13 +02:00
parent db030d6825
commit fd5dd4f3e7
5 changed files with 119 additions and 3 deletions

View File

@@ -109,8 +109,8 @@ This document describes the SQLite compatibility status of Limbo:
| substring(X,Y,Z) | No | |
| substring(X,Y) | No | |
| total_changes() | No | |
| trim(X) | No | |
| trim(X,Y) | No | |
| trim(X) | Yes | |
| trim(X,Y) | Yes | |
| typeof(X) | No | |
| unhex(X) | No | |
| unhex(X,Y) | No | |

View File

@@ -366,7 +366,43 @@ pub fn translate_expr(
dest: target_register,
func: SingleRowFunc::Random,
});
Ok(target_register)
}
SingleRowFunc::Trim => {
let args = if let Some(args) = args {
if args.len() > 2 {
anyhow::bail!(
"Parse error: trim function with more than 2 arguments"
);
}
args
} else {
anyhow::bail!("Parse error: trim 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::Trim,
});
} 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::Trim,
});
}
Ok(target_register)
}
}

View File

@@ -35,6 +35,7 @@ pub enum SingleRowFunc {
Upper,
Lower,
Random,
Trim,
}
impl ToString for SingleRowFunc {
@@ -46,6 +47,7 @@ impl ToString for SingleRowFunc {
SingleRowFunc::Upper => "upper".to_string(),
SingleRowFunc::Lower => "lower".to_string(),
SingleRowFunc::Random => "random".to_string(),
SingleRowFunc::Trim => "trim".to_string(),
}
}
}
@@ -74,6 +76,7 @@ impl FromStr for Func {
"upper" => Ok(Func::SingleRow(SingleRowFunc::Upper)),
"lower" => Ok(Func::SingleRow(SingleRowFunc::Lower)),
"random" => Ok(Func::SingleRow(SingleRowFunc::Random)),
"trim" => Ok(Func::SingleRow(SingleRowFunc::Trim)),
_ => Err(()),
}
}

View File

@@ -1293,6 +1293,16 @@ impl Program {
state.registers[*dest] = exec_random();
state.pc += 1;
}
SingleRowFunc::Trim => {
let start_reg = *start_reg;
let reg_value = state.registers[start_reg].clone();
let pattern_value = state.registers.get(start_reg + 1).cloned();
let result = exec_trim(&reg_value, pattern_value);
state.registers[*dest] = result;
state.pc += 1;
}
},
}
}
@@ -1878,6 +1888,23 @@ fn exec_like(pattern: &str, text: &str) -> bool {
re.is_match(text)
}
// Implements TRIM pattern matching.
fn exec_trim(reg: &OwnedValue, pattern: Option<OwnedValue>) -> OwnedValue {
match (reg, pattern) {
(reg, Some(pattern)) => match reg {
OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => {
let pattern_chars: Vec<char> = pattern.to_string().chars().collect();
OwnedValue::Text(Rc::new(
reg.to_string().trim_matches(&pattern_chars[..]).to_string(),
))
}
_ => reg.to_owned(),
},
(OwnedValue::Text(t), None) => OwnedValue::Text(Rc::new(t.trim().to_string())),
(reg, _) => reg.to_owned(),
}
}
// exec_if returns whether you should jump
fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
match reg {
@@ -1894,9 +1921,23 @@ 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_upper, OwnedValue};
use super::{
exec_abs, exec_if, exec_like, exec_lower, exec_random, exec_trim, exec_upper, OwnedValue,
};
use std::rc::Rc;
#[test]
fn test_trim() {
let input_str = OwnedValue::Text(Rc::new(String::from(" Bob and Alice ")));
let expected_str = OwnedValue::Text(Rc::new(String::from("Bob and Alice")));
assert_eq!(exec_trim(&input_str, None), expected_str);
let input_str = OwnedValue::Text(Rc::new(String::from(" Bob and Alice ")));
let pattern_str = OwnedValue::Text(Rc::new(String::from("Bob and")));
let expected_str = OwnedValue::Text(Rc::new(String::from("Alice")));
assert_eq!(exec_trim(&input_str, Some(pattern_str)), expected_str);
}
#[test]
fn test_upper_case() {
let input_str = OwnedValue::Text(Rc::new(String::from("Limbo")));

View File

@@ -50,3 +50,39 @@ do_execsql_test lower-char {
do_execsql_test lower-null {
select lower(null)
} {}
do_execsql_test trim {
SELECT trim(' Limbo ');
} {Limbo}
do_execsql_test trim-number {
SELECT trim(1);
} {1}
do_execsql_test trim-null {
SELECT trim(null);
} {}
do_execsql_test trim-leading-whitespace {
SELECT trim(' Leading');
} {Leading}
do_execsql_test trim-trailing-whitespace {
SELECT trim('Trailing ');
} {Trailing}
do_execsql_test trim-pattern {
SELECT trim('Limbo', 'Limbo');
} {}
do_execsql_test trim-pattern-number {
SELECT trim(1, '1');
} {}
do_execsql_test trim-pattern-null {
SELECT trim(null, 'null');
} {}
do_execsql_test trim-no-match-pattern {
SELECT trim('Limbo', 'xyz');
} {Limbo}