Skip to content

Commit

Permalink
Merge pull request #182 from brayanjuls/upper_lower_scalar_func
Browse files Browse the repository at this point in the history
implementation of scalar functions `upper` and `lower`
  • Loading branch information
penberg authored Jul 19, 2024
2 parents dd41a0c + 77b6f1c commit f794774
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 3 deletions.
4 changes: 2 additions & 2 deletions COMPAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ This document describes the SQLite compatibility status of Limbo:
| likely(X) | No | |
| load_extension(X) | No | |
| load_extension(X,Y) | No | |
| lower(X) | No | |
| lower(X) | Yes | |
| ltrim(X) | No | |
| ltrim(X,Y) | No | |
| max(X,Y,...) | No | |
Expand Down Expand Up @@ -116,7 +116,7 @@ This document describes the SQLite compatibility status of Limbo:
| unhex(X,Y) | No | |
| unicode(X) | No | |
| unlikely(X) | No | |
| upper(X) | No | |
| upper(X) | Yes | |
| zeroblob(N) | No | |

### Aggregate functions
Expand Down
44 changes: 44 additions & 0 deletions core/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,50 @@ pub fn translate_expr(
func: SingleRowFunc::Abs,
});

Ok(target_register)
}
SingleRowFunc::Upper => {
let args = if let Some(args) = args {
if args.len() != 1 {
anyhow::bail!(
"Parse error: upper function with not exactly 1 argument"
);
}
args
} else {
anyhow::bail!("Parse error: upper function with no arguments");
};

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::Upper,
});

Ok(target_register)
}
SingleRowFunc::Lower => {
let args = if let Some(args) = args {
if args.len() != 1 {
anyhow::bail!(
"Parse error: lower function with not exactly 1 argument"
);
}
args
} else {
anyhow::bail!("Parse error: lower function with no arguments");
};

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::Lower,
});

Ok(target_register)
}
}
Expand Down
6 changes: 6 additions & 0 deletions core/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pub enum SingleRowFunc {
Coalesce,
Like,
Abs,
Upper,
Lower,
}

impl ToString for SingleRowFunc {
Expand All @@ -40,6 +42,8 @@ impl ToString for SingleRowFunc {
SingleRowFunc::Coalesce => "coalesce".to_string(),
SingleRowFunc::Like => "like(2)".to_string(),
SingleRowFunc::Abs => "abs".to_string(),
SingleRowFunc::Upper => "upper".to_string(),
SingleRowFunc::Lower => "lower".to_string(),
}
}
}
Expand All @@ -65,6 +69,8 @@ impl FromStr for Func {
"coalesce" => Ok(Func::SingleRow(SingleRowFunc::Coalesce)),
"like" => Ok(Func::SingleRow(SingleRowFunc::Like)),
"abs" => Ok(Func::SingleRow(SingleRowFunc::Abs)),
"upper" => Ok(Func::SingleRow(SingleRowFunc::Upper)),
"lower" => Ok(Func::SingleRow(SingleRowFunc::Lower)),
_ => Err(()),
}
}
Expand Down
55 changes: 54 additions & 1 deletion core/vdbe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,24 @@ impl Program {
}
state.pc += 1;
}
SingleRowFunc::Upper => {
let reg_value = state.registers[*start_reg].borrow_mut();
if let Some(value) = exec_upper(reg_value) {
state.registers[*dest] = value;
} else {
state.registers[*dest] = OwnedValue::Null;
}
state.pc += 1;
}
SingleRowFunc::Lower => {
let reg_value = state.registers[*start_reg].borrow_mut();
if let Some(value) = exec_lower(reg_value) {
state.registers[*dest] = value;
} else {
state.registers[*dest] = OwnedValue::Null;
}
state.pc += 1;
}
},
}
}
Expand Down Expand Up @@ -1809,6 +1827,19 @@ fn get_indent_count(indent_count: usize, curr_insn: &Insn, prev_insn: Option<&In
}
}

fn exec_lower(reg: &OwnedValue) -> Option<OwnedValue> {
match reg {
OwnedValue::Text(t) => Some(OwnedValue::Text(Rc::new(t.to_lowercase()))),
t => Some(t.to_owned()),
}
}

fn exec_upper(reg: &OwnedValue) -> Option<OwnedValue> {
match reg {
OwnedValue::Text(t) => Some(OwnedValue::Text(Rc::new(t.to_uppercase()))),
t => Some(t.to_owned()),
}
}
fn exec_abs(reg: &OwnedValue) -> Option<OwnedValue> {
match reg {
OwnedValue::Integer(x) => {
Expand Down Expand Up @@ -1852,9 +1883,31 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {

#[cfg(test)]
mod tests {
use super::{exec_abs, exec_if, exec_like, OwnedValue};
use super::{exec_abs, exec_if, exec_like, exec_lower, exec_upper, OwnedValue};
use std::rc::Rc;

#[test]
fn test_upper_case() {
let input_str = OwnedValue::Text(Rc::new(String::from("Limbo")));
let expected_str = OwnedValue::Text(Rc::new(String::from("LIMBO")));
assert_eq!(exec_upper(&input_str).unwrap(), expected_str);

let input_int = OwnedValue::Integer(10);
assert_eq!(exec_upper(&input_int).unwrap(), input_int);
assert_eq!(exec_upper(&OwnedValue::Null).unwrap(), OwnedValue::Null)
}

#[test]
fn test_lower_case() {
let input_str = OwnedValue::Text(Rc::new(String::from("Limbo")));
let expected_str = OwnedValue::Text(Rc::new(String::from("limbo")));
assert_eq!(exec_lower(&input_str).unwrap(), expected_str);

let input_int = OwnedValue::Integer(10);
assert_eq!(exec_lower(&input_int).unwrap(), input_int);
assert_eq!(exec_lower(&OwnedValue::Null).unwrap(), OwnedValue::Null)
}

#[test]
fn test_abs() {
let int_positive_reg = OwnedValue::Integer(10);
Expand Down
32 changes: 32 additions & 0 deletions testing/scalar-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,35 @@ do_execsql_test abs-char {
do_execsql_test abs-null {
select abs(null);
} {}

do_execsql_test upper {
select upper('Limbo')
} {LIMBO}

do_execsql_test upper-number {
select upper(1)
} {1}

do_execsql_test upper-char {
select upper('a')
} {A}

do_execsql_test upper-null {
select upper(null)
} {}

do_execsql_test lower {
select lower('Limbo')
} {limbo}

do_execsql_test lower-number {
select lower(1)
} {1}

do_execsql_test lower-char {
select lower('A')
} {a}

do_execsql_test lower-null {
select lower(null)
} {}

0 comments on commit f794774

Please sign in to comment.