Skip to main content

reifydb_function/text/
concat.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use 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		// Validate all arguments are Utf8
35		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}