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

Add support for concat scalar function

### EXPLAIN SELECT concat('a', 1.5, 'b')
![image](https://github.com/user-attachments/assets/628e0e81-91bc-4533-9091-ded99dd20e0e)

## Related issue
https://github.com/penberg/limbo/issues/144

Closes #291
This commit is contained in:
Pekka Enberg
2024-08-16 16:37:38 +03:00
5 changed files with 60 additions and 2 deletions

View File

@@ -65,7 +65,7 @@ This document describes the SQLite compatibility status of Limbo:
| changes() | No | |
| char(X1,X2,...,XN) | Yes | |
| coalesce(X,Y,...) | Yes | |
| concat(X,...) | No | |
| concat(X,...) | Yes | |
| concat_ws(SEP,X,...) | No | |
| format(FORMAT,...) | No | |
| glob(X,Y) | No | |
@@ -165,7 +165,7 @@ This document describes the SQLite compatibility status of Limbo:
| json_insert(json,path,value,...) | | |
| jsonb_insert(json,path,value,...) | | |
| json_object(label1,value1,...) | | |
| jsonb_object(label1,value1,...)   | | |
| jsonb_object(label1,value1,...) | | |
| json_patch(json1,json2) | | |
| jsonb_patch(json1,json2) | | |
| json_pretty(json) | | |

View File

@@ -42,6 +42,7 @@ impl AggFunc {
pub enum ScalarFunc {
Char,
Coalesce,
Concat,
IfNull,
Like,
Abs,
@@ -67,6 +68,7 @@ impl ToString for ScalarFunc {
match self {
ScalarFunc::Char => "char".to_string(),
ScalarFunc::Coalesce => "coalesce".to_string(),
ScalarFunc::Concat => "concat".to_string(),
ScalarFunc::IfNull => "ifnull".to_string(),
ScalarFunc::Like => "like(2)".to_string(),
ScalarFunc::Abs => "abs".to_string(),
@@ -111,6 +113,7 @@ impl Func {
"total" => Ok(Func::Agg(AggFunc::Total)),
"char" => Ok(Func::Scalar(ScalarFunc::Char)),
"coalesce" => Ok(Func::Scalar(ScalarFunc::Coalesce)),
"concat" => Ok(Func::Scalar(ScalarFunc::Concat)),
"ifnull" => Ok(Func::Scalar(ScalarFunc::IfNull)),
"like" => Ok(Func::Scalar(ScalarFunc::Like)),
"abs" => Ok(Func::Scalar(ScalarFunc::Abs)),

View File

@@ -220,6 +220,26 @@ pub fn translate_expr(
Ok(target_register)
}
ScalarFunc::Concat => {
let args = if let Some(args) = args {
args
} else {
crate::bail_parse_error!(
"{} function with no arguments",
srf.to_string()
);
};
for arg in args.iter() {
let reg = program.alloc_register();
translate_expr(program, select, arg, reg, cursor_hint)?;
}
program.emit_insn(Insn::Function {
start_reg: target_register,
dest: target_register,
func: crate::vdbe::Func::Scalar(srf),
});
Ok(target_register)
}
ScalarFunc::IfNull => {
let args = match args {
Some(args) if args.len() == 2 => args,

View File

@@ -1192,6 +1192,12 @@ impl Program {
state.pc += 1;
}
Func::Scalar(ScalarFunc::Coalesce) => {}
Func::Scalar(ScalarFunc::Concat) => {
let start_reg = *start_reg;
let result = exec_concat(&state.registers[start_reg..]);
state.registers[*dest] = result;
state.pc += 1;
}
Func::Scalar(ScalarFunc::IfNull) => {}
Func::Scalar(ScalarFunc::Like) => {
let start_reg = *start_reg;
@@ -1615,6 +1621,19 @@ fn exec_upper(reg: &OwnedValue) -> Option<OwnedValue> {
}
}
fn exec_concat(registers: &[OwnedValue]) -> OwnedValue {
let mut result = String::new();
for reg in registers {
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,6 +3,22 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test concat-1 {
select concat('l', 'i');
} {li}
do_execsql_test concat-2 {
select concat('l', 1);
} {l1}
do_execsql_test concat-3 {
select concat('l', 1.5);
} {l1.5}
do_execsql_test concat-4 {
select concat('l', null, 'i');
} {li}
do_execsql_test char {
select char(108, 105)
} {li}