Added Concat Opcode

This commit is contained in:
Harin 2025-01-20 23:49:50 +05:30
parent c27427d644
commit da53cc3821
7 changed files with 80 additions and 3 deletions

View file

@ -332,7 +332,7 @@ Modifiers:
| CollSeq | No | | CollSeq | No |
| Column | Yes | | Column | Yes |
| Compare | Yes | | Compare | Yes |
| Concat | No | | Concat | Yes |
| Copy | Yes | | Copy | Yes |
| Count | No | | Count | No |
| CreateIndex | No | | CreateIndex | No |

View file

@ -679,6 +679,13 @@ pub fn translate_expr(
}, },
}) })
} }
ast::Operator::Concat => {
program.emit_insn(Insn::Concat {
lhs: e1_reg,
rhs: e2_reg,
dest: target_register,
});
}
other_unimplemented => todo!("{:?}", other_unimplemented), other_unimplemented => todo!("{:?}", other_unimplemented),
} }
Ok(target_register) Ok(target_register)

View file

@ -72,6 +72,18 @@ impl OwnedValue {
pub fn build_text(text: Rc<String>) -> Self { pub fn build_text(text: Rc<String>) -> Self {
Self::Text(LimboText::new(text)) Self::Text(LimboText::new(text))
} }
pub fn value_to_string(value: &OwnedValue) -> String {
match value {
OwnedValue::Text(text) => text.value.as_ref().clone(),
OwnedValue::Integer(i) => i.to_string(),
OwnedValue::Float(f) => f.to_string(),
OwnedValue::Agg(aggctx) => aggctx.final_value().to_string(),
OwnedValue::Null => String::new(),
OwnedValue::Blob(_) => todo!("TODO: Handle Blob conversion to String"),
OwnedValue::Record(_) => unreachable!(),
}
}
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]

View file

@ -1111,6 +1111,15 @@ pub fn insn_to_str(
0, 0,
format!("r[{}]=!r[{}]", dest, reg), format!("r[{}]=!r[{}]", dest, reg),
), ),
Insn::Concat { lhs, rhs, dest } => (
"Concat",
*rhs as i32,
*lhs as i32,
*dest as i32,
OwnedValue::build_text(Rc::new("".to_string())),
0,
format!("r[{}]=r[{}] << r[{}]", dest, lhs, rhs)
),
}; };
format!( format!(
"{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}", "{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}",

View file

@ -1,4 +1,5 @@
use std::num::NonZero; use std::num::NonZero;
use std::rc::Rc;
use super::{AggFunc, BranchOffset, CursorID, FuncCtx, PageIdx}; use super::{AggFunc, BranchOffset, CursorID, FuncCtx, PageIdx};
use crate::storage::wal::CheckpointMode; use crate::storage::wal::CheckpointMode;
@ -544,6 +545,12 @@ pub enum Insn {
reg: usize, reg: usize,
dest: usize, dest: usize,
}, },
/// Concatenates the `rhs` and `lhs` values and stores the result in the third register.
Concat {
lhs: usize,
rhs: usize,
dest: usize,
},
} }
fn cast_text_to_numerical(value: &str) -> OwnedValue { fn cast_text_to_numerical(value: &str) -> OwnedValue {
@ -886,3 +893,15 @@ pub fn exec_boolean_not(mut reg: &OwnedValue) -> OwnedValue {
_ => todo!(), _ => todo!(),
} }
} }
pub fn exec_concat(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue {
let lhs_value = OwnedValue::value_to_string(lhs);
let rhs_value = OwnedValue::value_to_string(rhs);
if lhs_value.is_empty() || rhs_value.is_empty() {
OwnedValue::Null
} else {
let result = lhs_value + &rhs_value;
OwnedValue::build_text(Rc::new(result))
}
}

View file

@ -48,6 +48,7 @@ use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_un
use insn::{ use insn::{
exec_add, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_divide, exec_add, exec_bit_and, exec_bit_not, exec_bit_or, exec_boolean_not, exec_divide,
exec_multiply, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract, exec_multiply, exec_remainder, exec_shift_left, exec_shift_right, exec_subtract,
exec_concat
}; };
use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape}; use likeop::{construct_like_escape_arg, exec_glob, exec_like_with_escape};
use rand::distributions::{Distribution, Uniform}; use rand::distributions::{Distribution, Uniform};
@ -1724,7 +1725,7 @@ impl Program {
} }
ScalarFunc::Coalesce => {} ScalarFunc::Coalesce => {}
ScalarFunc::Concat => { ScalarFunc::Concat => {
let result = exec_concat( let result = exec_concat_strings(
&state.registers[*start_reg..*start_reg + arg_count], &state.registers[*start_reg..*start_reg + arg_count],
); );
state.registers[*dest] = result; state.registers[*dest] = result;
@ -2341,6 +2342,10 @@ impl Program {
state.registers[*dest] = exec_boolean_not(&state.registers[*reg]); state.registers[*dest] = exec_boolean_not(&state.registers[*reg]);
state.pc += 1; state.pc += 1;
} }
Insn::Concat { lhs, rhs, dest } => {
state.registers[*dest] = exec_concat(&state.registers[*lhs], &state.registers[*rhs]);
state.pc += 1;
}
} }
} }
} }
@ -2476,7 +2481,7 @@ fn exec_upper(reg: &OwnedValue) -> Option<OwnedValue> {
} }
} }
fn exec_concat(registers: &[OwnedValue]) -> OwnedValue { fn exec_concat_strings(registers: &[OwnedValue]) -> OwnedValue {
let mut result = String::new(); let mut result = String::new();
for reg in registers { for reg in registers {
match reg { match reg {

25
testing/concat.test Normal file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env tclsh
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test concat {
SELECT 'Hello' || ' ' || 'World';
} {"Hello World"}
do_execsql_test concat-2 {
SELECT 'Hello' || NULL;
} {}
do_execsql_test concat-3 {
SELECT 'Hello' || NULL;
} {}
do_execsql_test concat-4 {
SELECT 'A good ' || name FROM products WHERE name = 'hat';
} {"A good hat"}
do_execsql_test concat-4 {
SELECT 'A good ' || name FROM products WHERE name = 'watch';
} {}