reifydb_function/text/
concat.rs1use reifydb_core::value::column::data::ColumnData;
5use reifydb_type::value::{container::utf8::Utf8Container, r#type::Type};
6
7use crate::{ScalarFunction, ScalarFunctionContext, error::ScalarFunctionError, propagate_options};
8
9pub struct TextConcat;
10
11impl TextConcat {
12 pub fn new() -> Self {
13 Self
14 }
15}
16
17impl ScalarFunction for TextConcat {
18 fn scalar(&self, ctx: ScalarFunctionContext) -> crate::error::ScalarFunctionResult<ColumnData> {
19 if let Some(result) = propagate_options(self, &ctx) {
20 return result;
21 }
22
23 let columns = ctx.columns;
24 let row_count = ctx.row_count;
25
26 if columns.len() < 2 {
27 return Err(ScalarFunctionError::ArityMismatch {
28 function: ctx.fragment.clone(),
29 expected: 2,
30 actual: columns.len(),
31 });
32 }
33
34 for (idx, col) in columns.iter().enumerate() {
36 match col.data() {
37 ColumnData::Utf8 {
38 ..
39 } => {}
40 other => {
41 return Err(ScalarFunctionError::InvalidArgumentType {
42 function: ctx.fragment.clone(),
43 argument_index: idx,
44 expected: vec![Type::Utf8],
45 actual: other.get_type(),
46 });
47 }
48 }
49 }
50
51 let mut result_data = Vec::with_capacity(row_count);
52
53 for i in 0..row_count {
54 let mut all_defined = true;
55 let mut concatenated = String::new();
56
57 for col in columns.iter() {
58 if let ColumnData::Utf8 {
59 container,
60 ..
61 } = col.data()
62 {
63 if container.is_defined(i) {
64 concatenated.push_str(&container[i]);
65 } else {
66 all_defined = false;
67 break;
68 }
69 }
70 }
71
72 if all_defined {
73 result_data.push(concatenated);
74 } else {
75 result_data.push(String::new());
76 }
77 }
78
79 Ok(ColumnData::Utf8 {
80 container: Utf8Container::new(result_data),
81 max_bytes: reifydb_type::value::constraint::bytes::MaxBytes::MAX,
82 })
83 }
84
85 fn return_type(&self, _input_types: &[Type]) -> Type {
86 Type::Utf8
87 }
88}