mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-30 13:24:22 +01:00
Merge 'Add support for nullif scalar function' from Kim Seon Woo
### EXPLAIN nullif(1,2) <img width="1339" alt="image" src="https://github.com/user-attachments/assets/08230797-914d-4922-b52c-5b2b2b4c2a12"> ### Issue https://github.com/penberg/limbo/issues/144 Closes #299
This commit is contained in:
@@ -56,6 +56,7 @@ pub enum ScalarFunc {
|
||||
Length,
|
||||
Min,
|
||||
Max,
|
||||
Nullif,
|
||||
Substr,
|
||||
Substring,
|
||||
Date,
|
||||
@@ -83,6 +84,7 @@ impl ToString for ScalarFunc {
|
||||
ScalarFunc::Length => "length".to_string(),
|
||||
ScalarFunc::Min => "min".to_string(),
|
||||
ScalarFunc::Max => "max".to_string(),
|
||||
ScalarFunc::Nullif => "nullif".to_string(),
|
||||
ScalarFunc::Substr => "substr".to_string(),
|
||||
ScalarFunc::Substring => "substring".to_string(),
|
||||
ScalarFunc::Date => "date".to_string(),
|
||||
@@ -110,6 +112,7 @@ impl Func {
|
||||
"max" if arg_count > 1 => Ok(Func::Scalar(ScalarFunc::Max)),
|
||||
"min" if arg_count == 0 || arg_count == 1 => Ok(Func::Agg(AggFunc::Min)),
|
||||
"min" if arg_count > 1 => Ok(Func::Scalar(ScalarFunc::Min)),
|
||||
"nullif" if arg_count == 2 => Ok(Func::Scalar(ScalarFunc::Nullif)),
|
||||
"string_agg" => Ok(Func::Agg(AggFunc::StringAgg)),
|
||||
"sum" => Ok(Func::Agg(AggFunc::Sum)),
|
||||
"total" => Ok(Func::Agg(AggFunc::Total)),
|
||||
|
||||
@@ -1067,6 +1067,43 @@ pub fn translate_expr(
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
ScalarFunc::Nullif => {
|
||||
let args = if let Some(args) = args {
|
||||
if args.len() != 2 {
|
||||
crate::bail_parse_error!(
|
||||
"nullif function must have two argument"
|
||||
);
|
||||
}
|
||||
args
|
||||
} else {
|
||||
crate::bail_parse_error!("nullif function with no arguments");
|
||||
};
|
||||
|
||||
let func_reg = program.alloc_register();
|
||||
let first_reg = program.alloc_register();
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[0],
|
||||
first_reg,
|
||||
cursor_hint,
|
||||
)?;
|
||||
let second_reg = program.alloc_register();
|
||||
translate_expr(
|
||||
program,
|
||||
referenced_tables,
|
||||
&args[1],
|
||||
second_reg,
|
||||
cursor_hint,
|
||||
)?;
|
||||
program.emit_insn(Insn::Function {
|
||||
start_reg: func_reg,
|
||||
dest: target_register,
|
||||
func: crate::vdbe::Func::Scalar(srf),
|
||||
});
|
||||
|
||||
Ok(target_register)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -1321,6 +1321,13 @@ impl Program {
|
||||
}
|
||||
state.pc += 1;
|
||||
}
|
||||
Func::Scalar(ScalarFunc::Nullif) => {
|
||||
let start_reg = *start_reg;
|
||||
let first_value = &state.registers[start_reg + 1];
|
||||
let second_value = &state.registers[start_reg + 2];
|
||||
state.registers[*dest] = exec_nullif(first_value, second_value);
|
||||
state.pc += 1;
|
||||
}
|
||||
Func::Scalar(ScalarFunc::Substr) | Func::Scalar(ScalarFunc::Substring) => {
|
||||
let start_reg = *start_reg;
|
||||
let str_value = &state.registers[start_reg];
|
||||
@@ -1717,6 +1724,14 @@ fn exec_minmax<'a>(
|
||||
regs.into_iter().reduce(|a, b| op(a, b)).cloned()
|
||||
}
|
||||
|
||||
fn exec_nullif(first_value: &OwnedValue, second_value: &OwnedValue) -> OwnedValue {
|
||||
if first_value != second_value {
|
||||
first_value.clone()
|
||||
} else {
|
||||
OwnedValue::Null
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_substring(
|
||||
str_value: &OwnedValue,
|
||||
start_value: &OwnedValue,
|
||||
@@ -1867,9 +1882,9 @@ fn exec_if(reg: &OwnedValue, null_reg: &OwnedValue, not: bool) -> bool {
|
||||
mod tests {
|
||||
use super::{
|
||||
exec_abs, exec_char, exec_if, exec_length, exec_like, exec_lower, exec_ltrim, exec_minmax,
|
||||
exec_quote, exec_random, exec_round, exec_rtrim, exec_substring, exec_trim, exec_unicode,
|
||||
exec_upper, get_new_rowid, Cursor, CursorResult, LimboError, OwnedRecord, OwnedValue,
|
||||
Result,
|
||||
exec_nullif, exec_quote, exec_random, exec_round, exec_rtrim, exec_substring, exec_trim,
|
||||
exec_unicode, exec_upper, get_new_rowid, Cursor, CursorResult, LimboError, OwnedRecord,
|
||||
OwnedValue, Result,
|
||||
};
|
||||
use mockall::{mock, predicate};
|
||||
use rand::{rngs::mock::StepRng, thread_rng};
|
||||
@@ -2298,6 +2313,41 @@ mod tests {
|
||||
assert!(!exec_if(®, &null_reg, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nullif() {
|
||||
assert_eq!(
|
||||
exec_nullif(&OwnedValue::Integer(1), &OwnedValue::Integer(1)),
|
||||
OwnedValue::Null
|
||||
);
|
||||
assert_eq!(
|
||||
exec_nullif(&OwnedValue::Float(1.1), &OwnedValue::Float(1.1)),
|
||||
OwnedValue::Null
|
||||
);
|
||||
assert_eq!(
|
||||
exec_nullif(
|
||||
&OwnedValue::Text(Rc::new("limbo".to_string())),
|
||||
&OwnedValue::Text(Rc::new("limbo".to_string()))
|
||||
),
|
||||
OwnedValue::Null
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
exec_nullif(&OwnedValue::Integer(1), &OwnedValue::Integer(2)),
|
||||
OwnedValue::Integer(1)
|
||||
);
|
||||
assert_eq!(
|
||||
exec_nullif(&OwnedValue::Float(1.1), &OwnedValue::Float(1.2)),
|
||||
OwnedValue::Float(1.1)
|
||||
);
|
||||
assert_eq!(
|
||||
exec_nullif(
|
||||
&OwnedValue::Text(Rc::new("limbo".to_string())),
|
||||
&OwnedValue::Text(Rc::new("limb".to_string()))
|
||||
),
|
||||
OwnedValue::Text(Rc::new("limbo".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_substring() {
|
||||
let str_value = OwnedValue::Text(Rc::new("limbo".to_string()));
|
||||
|
||||
Reference in New Issue
Block a user