mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-27 04:54:21 +01:00
Merge pull request #188 from JeanArhancet/feat/add-length-scalar-function
This commit is contained in:
174
core/expr.rs
174
core/expr.rs
@@ -222,10 +222,8 @@ pub fn translate_expr(
|
||||
args,
|
||||
filter_over: _,
|
||||
} => {
|
||||
let func_type: Option<Func> = match normalize_ident(name.0.as_str()).as_str().parse() {
|
||||
Ok(func) => Some(func),
|
||||
Err(_) => None,
|
||||
};
|
||||
let func_type: Option<Func> = normalize_ident(name.0.as_str()).as_str().parse().ok();
|
||||
|
||||
match func_type {
|
||||
Some(Func::Agg(_)) => {
|
||||
anyhow::bail!("Parse error: aggregation function in non-aggregation context")
|
||||
@@ -236,12 +234,16 @@ pub fn translate_expr(
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() < 2 {
|
||||
anyhow::bail!(
|
||||
"Parse error: coalesce function with less than 2 arguments"
|
||||
"Parse error: {} function with less than 2 arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
anyhow::bail!("Parse error: coalesce function with no arguments");
|
||||
anyhow::bail!(
|
||||
"Parse error: {} function with no arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
// coalesce function is implemented as a series of not null checks
|
||||
@@ -267,16 +269,20 @@ pub fn translate_expr(
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() < 2 {
|
||||
anyhow::bail!(
|
||||
"Parse error: like function with less than 2 arguments"
|
||||
"Parse error: {} function with less than 2 arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
anyhow::bail!("Parse error: like function with no arguments");
|
||||
anyhow::bail!(
|
||||
"Parse error: {} function with no arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
for arg in args {
|
||||
let reg = program.alloc_register();
|
||||
let _ = translate_expr(program, select, arg, reg)?;
|
||||
let _ = translate_expr(program, select, &arg, reg)?;
|
||||
match arg {
|
||||
ast::Expr::Literal(_) => program.mark_last_insn_constant(),
|
||||
_ => {}
|
||||
@@ -285,161 +291,81 @@ pub fn translate_expr(
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: target_register + 1,
|
||||
dest: target_register,
|
||||
func: SingleRowFunc::Like,
|
||||
func: srf,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
SingleRowFunc::Abs => {
|
||||
SingleRowFunc::Abs
|
||||
| SingleRowFunc::Lower
|
||||
| SingleRowFunc::Upper
|
||||
| SingleRowFunc::Length => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() != 1 {
|
||||
anyhow::bail!(
|
||||
"Parse error: abs function with not exactly 1 argument"
|
||||
"Parse error: {} function with not exactly 1 argument",
|
||||
srf.to_string()
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
anyhow::bail!("Parse error: abs function with no arguments");
|
||||
anyhow::bail!(
|
||||
"Parse error: {} function with no arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
let regs = program.alloc_register();
|
||||
let _ = translate_expr(program, select, &args[0], regs)?;
|
||||
translate_expr(program, select, &args[0], regs)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: regs,
|
||||
dest: target_register,
|
||||
func: SingleRowFunc::Abs,
|
||||
func: srf,
|
||||
});
|
||||
|
||||
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)
|
||||
}
|
||||
SingleRowFunc::Random => {
|
||||
if args.is_some() {
|
||||
anyhow::bail!("Parse error: random function with arguments");
|
||||
anyhow::bail!(
|
||||
"Parse error: {} function with arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
}
|
||||
let regs = program.alloc_register();
|
||||
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: regs,
|
||||
dest: target_register,
|
||||
func: SingleRowFunc::Random,
|
||||
func: srf,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
SingleRowFunc::Trim => {
|
||||
SingleRowFunc::Trim | SingleRowFunc::Round => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() > 2 {
|
||||
anyhow::bail!(
|
||||
"Parse error: trim function with more than 2 arguments"
|
||||
"Parse error: {} function with more than 2 arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
anyhow::bail!("Parse error: trim function with no arguments");
|
||||
anyhow::bail!(
|
||||
"Parse error: {} function with no arguments",
|
||||
srf.to_string()
|
||||
);
|
||||
};
|
||||
|
||||
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(),
|
||||
_ => {}
|
||||
}
|
||||
for arg in args.iter() {
|
||||
let reg = program.alloc_register();
|
||||
translate_expr(program, select, arg, reg)?;
|
||||
if let ast::Expr::Literal(_) = arg {
|
||||
program.mark_last_insn_constant();
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: target_register + 1,
|
||||
dest: target_register,
|
||||
func: SingleRowFunc::Trim,
|
||||
});
|
||||
}
|
||||
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,
|
||||
});
|
||||
}
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: target_register + 1,
|
||||
dest: target_register,
|
||||
func: srf,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ pub enum SingleRowFunc {
|
||||
Random,
|
||||
Trim,
|
||||
Round,
|
||||
Length,
|
||||
}
|
||||
|
||||
impl ToString for SingleRowFunc {
|
||||
@@ -50,6 +51,7 @@ impl ToString for SingleRowFunc {
|
||||
SingleRowFunc::Random => "random".to_string(),
|
||||
SingleRowFunc::Trim => "trim".to_string(),
|
||||
SingleRowFunc::Round => "round".to_string(),
|
||||
SingleRowFunc::Length => "length".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,6 +82,7 @@ impl FromStr for Func {
|
||||
"random" => Ok(Func::SingleRow(SingleRowFunc::Random)),
|
||||
"trim" => Ok(Func::SingleRow(SingleRowFunc::Trim)),
|
||||
"round" => Ok(Func::SingleRow(SingleRowFunc::Round)),
|
||||
"length" => Ok(Func::SingleRow(SingleRowFunc::Length)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
40
core/vdbe.rs
40
core/vdbe.rs
@@ -1289,6 +1289,11 @@ impl Program {
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
SingleRowFunc::Length => {
|
||||
let reg_value = state.registers[*start_reg].borrow_mut();
|
||||
state.registers[*dest] = exec_length(reg_value);
|
||||
state.pc += 1;
|
||||
}
|
||||
SingleRowFunc::Random => {
|
||||
state.registers[*dest] = exec_random();
|
||||
state.pc += 1;
|
||||
@@ -1856,12 +1861,23 @@ fn exec_lower(reg: &OwnedValue) -> Option<OwnedValue> {
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_length(reg: &OwnedValue) -> OwnedValue {
|
||||
match reg {
|
||||
OwnedValue::Text(_) | OwnedValue::Integer(_) | OwnedValue::Float(_) => {
|
||||
OwnedValue::Integer(reg.to_string().len() as i64)
|
||||
}
|
||||
OwnedValue::Blob(blob) => OwnedValue::Integer(blob.len() as i64),
|
||||
_ => reg.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) => {
|
||||
@@ -1951,11 +1967,31 @@ 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_round, exec_trim, exec_upper,
|
||||
OwnedValue,
|
||||
exec_abs, exec_if, exec_length, exec_like, exec_lower, exec_random, exec_round, exec_trim,
|
||||
exec_upper, OwnedValue,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn test_length() {
|
||||
let input_str = OwnedValue::Text(Rc::new(String::from("bob")));
|
||||
let expected_len = OwnedValue::Integer(3);
|
||||
assert_eq!(exec_length(&input_str), expected_len);
|
||||
|
||||
let input_integer = OwnedValue::Integer(123);
|
||||
let expected_len = OwnedValue::Integer(3);
|
||||
assert_eq!(exec_length(&input_integer), expected_len);
|
||||
|
||||
let input_float = OwnedValue::Float(123.456);
|
||||
let expected_len = OwnedValue::Integer(7);
|
||||
assert_eq!(exec_length(&input_float), expected_len);
|
||||
|
||||
// Expected byte array for "example"
|
||||
let expected_blob = OwnedValue::Blob(Rc::new(vec![101, 120, 97, 109, 112, 108, 101]));
|
||||
let expected_len = OwnedValue::Integer(7);
|
||||
assert_eq!(exec_length(&expected_blob), expected_len);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trim() {
|
||||
let input_str = OwnedValue::Text(Rc::new(String::from(" Bob and Alice ")));
|
||||
|
||||
@@ -122,3 +122,23 @@ do_execsql_test round-float-zero-precision {
|
||||
do_execsql_test round-null-precision {
|
||||
SELECT round(123.456, null);
|
||||
} {}
|
||||
|
||||
do_execsql_test length-text {
|
||||
SELECT length('limbo');
|
||||
} {5}
|
||||
|
||||
do_execsql_test length-integer {
|
||||
SELECT length(12345);
|
||||
} {5}
|
||||
|
||||
do_execsql_test length-float {
|
||||
SELECT length(123.456);
|
||||
} {7}
|
||||
|
||||
do_execsql_test length-null {
|
||||
SELECT length(NULL);
|
||||
} {}
|
||||
|
||||
do_execsql_test length-empty-text {
|
||||
SELECT length('');
|
||||
} {0}
|
||||
Reference in New Issue
Block a user