Merge 'Add support for concat_ws scalar function' from Kim Seon Woo

Adding support for `concat_ws`
<img width="1604" alt="image" src="https://github.com/user-
attachments/assets/8281d0c1-c338-4695-aa17-3b86f16625d3">
## Related issue
https://github.com/penberg/limbo/issues/144

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #324
This commit is contained in:
jussisaurio
2024-09-15 09:09:40 +03:00
5 changed files with 98 additions and 6 deletions

View File

@@ -66,7 +66,7 @@ This document describes the SQLite compatibility status of Limbo:
| char(X1,X2,...,XN) | Yes | |
| coalesce(X,Y,...) | Yes | |
| concat(X,...) | Yes | |
| concat_ws(SEP,X,...) | No | |
| concat_ws(SEP,X,...) | Yes | |
| format(FORMAT,...) | No | |
| glob(X,Y) | No | |
| hex(X) | No | |

View File

@@ -43,6 +43,7 @@ pub enum ScalarFunc {
Char,
Coalesce,
Concat,
ConcatWs,
IfNull,
Like,
Abs,
@@ -72,6 +73,7 @@ impl ToString for ScalarFunc {
ScalarFunc::Char => "char".to_string(),
ScalarFunc::Coalesce => "coalesce".to_string(),
ScalarFunc::Concat => "concat".to_string(),
ScalarFunc::ConcatWs => "concat_ws".to_string(),
ScalarFunc::IfNull => "ifnull".to_string(),
ScalarFunc::Like => "like(2)".to_string(),
ScalarFunc::Abs => "abs".to_string(),
@@ -137,6 +139,7 @@ impl Func {
"char" => Ok(Func::Scalar(ScalarFunc::Char)),
"coalesce" => Ok(Func::Scalar(ScalarFunc::Coalesce)),
"concat" => Ok(Func::Scalar(ScalarFunc::Concat)),
"concat_ws" => Ok(Func::Scalar(ScalarFunc::ConcatWs)),
"ifnull" => Ok(Func::Scalar(ScalarFunc::IfNull)),
"like" => Ok(Func::Scalar(ScalarFunc::Like)),
"abs" => Ok(Func::Scalar(ScalarFunc::Abs)),

View File

@@ -864,6 +864,45 @@ pub fn translate_expr(
});
Ok(target_register)
}
ScalarFunc::ConcatWs => {
let args = match args {
Some(args) if args.len() >= 2 => args,
Some(_) => crate::bail_parse_error!(
"{} function requires at least 2 arguments",
srf.to_string()
),
None => crate::bail_parse_error!(
"{} function requires arguments",
srf.to_string()
),
};
let temp_register = program.alloc_register();
for arg in args.iter() {
let reg = program.alloc_register();
translate_expr(
program,
referenced_tables,
arg,
reg,
cursor_hint,
cached_results,
)?;
}
program.emit_insn(Insn::Function {
constant_mask: 0,
start_reg: temp_register + 1,
dest: temp_register,
func: func_ctx,
});
program.emit_insn(Insn::Copy {
src_reg: temp_register,
dst_reg: target_register,
amount: 1,
});
Ok(target_register)
}
ScalarFunc::IfNull => {
let args = match args {
Some(args) if args.len() == 2 => args,

View File

@@ -1434,6 +1434,12 @@ impl Program {
);
state.registers[*dest] = result;
}
ScalarFunc::ConcatWs => {
let result = exec_concat_ws(
&state.registers[*start_reg..*start_reg + arg_count],
);
state.registers[*dest] = result;
}
ScalarFunc::IfNull => {}
ScalarFunc::Like => {
let pattern = &state.registers[*start_reg];
@@ -1828,6 +1834,34 @@ fn exec_concat(registers: &[OwnedValue]) -> OwnedValue {
OwnedValue::Text(Rc::new(result))
}
fn exec_concat_ws(registers: &[OwnedValue]) -> OwnedValue {
if registers.is_empty() {
return OwnedValue::Null;
}
let separator = match &registers[0] {
OwnedValue::Text(text) => text.clone(),
OwnedValue::Integer(i) => Rc::new(i.to_string()),
OwnedValue::Float(f) => Rc::new(f.to_string()),
_ => return OwnedValue::Null,
};
let mut result = String::new();
for (i, reg) in registers.iter().enumerate().skip(1) {
if i > 1 {
result.push_str(&separator);
}
match reg {
OwnedValue::Text(text) => result.push_str(text),
OwnedValue::Integer(i) => result.push_str(&i.to_string()),
OwnedValue::Float(f) => result.push_str(&f.to_string()),
_ => continue,
}
}
OwnedValue::Text(Rc::new(result))
}
fn exec_abs(reg: &OwnedValue) -> Option<OwnedValue> {
match reg {
OwnedValue::Integer(x) => {

View File

@@ -3,22 +3,38 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test concat-1 {
do_execsql_test concat-chars {
select concat('l', 'i');
} {li}
do_execsql_test concat-2 {
do_execsql_test concat-char-and-number {
select concat('l', 1);
} {l1}
do_execsql_test concat-3 {
do_execsql_test concat-char-and-decimal {
select concat('l', 1.5);
} {l1.5}
do_execsql_test concat-4 {
do_execsql_test concat-char-null-char {
select concat('l', null, 'i');
} {li}
do_execsql_test concat_ws-numbers {
select concat_ws(',', 1, 2);
} {1,2}
do_execsql_test concat_ws-single-number {
select concat_ws(',', 1);
} {1}
do_execsql_test concat_ws-null {
select concat_ws(null, 1, 2);
} {}
do_execsql_test concat_ws-multiple {
select concat_ws(',', 1, 2), concat_ws(',', 3, 4)
} {1,2|3,4}
do_execsql_test char {
select char(108, 105)
} {li}
@@ -369,4 +385,4 @@ do_execsql_test quote-null {
do_execsql_test quote-integer {
SELECT quote(123)
} {123}
} {123}