Skip to main content

reifydb_function/text/
contains.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::r#type::Type;
6
7use crate::{
8	ScalarFunction, ScalarFunctionContext,
9	error::{ScalarFunctionError, ScalarFunctionResult},
10	propagate_options,
11};
12
13pub struct TextContains;
14
15impl TextContains {
16	pub fn new() -> Self {
17		Self
18	}
19}
20
21impl ScalarFunction for TextContains {
22	fn scalar(&self, ctx: ScalarFunctionContext) -> ScalarFunctionResult<ColumnData> {
23		if let Some(result) = propagate_options(self, &ctx) {
24			return result;
25		}
26
27		let columns = ctx.columns;
28		let row_count = ctx.row_count;
29
30		if columns.len() != 2 {
31			return Err(ScalarFunctionError::ArityMismatch {
32				function: ctx.fragment.clone(),
33				expected: 2,
34				actual: columns.len(),
35			});
36		}
37
38		let str_col = columns.get(0).unwrap();
39		let substr_col = columns.get(1).unwrap();
40
41		match (str_col.data(), substr_col.data()) {
42			(
43				ColumnData::Utf8 {
44					container: str_container,
45					..
46				},
47				ColumnData::Utf8 {
48					container: substr_container,
49					..
50				},
51			) => {
52				let mut result_data = Vec::with_capacity(row_count);
53				let mut result_bitvec = Vec::with_capacity(row_count);
54
55				for i in 0..row_count {
56					if str_container.is_defined(i) && substr_container.is_defined(i) {
57						let s = &str_container[i];
58						let substr = &substr_container[i];
59						result_data.push(s.contains(substr.as_str()));
60						result_bitvec.push(true);
61					} else {
62						result_data.push(false);
63						result_bitvec.push(false);
64					}
65				}
66
67				Ok(ColumnData::bool_with_bitvec(result_data, result_bitvec))
68			}
69			(
70				ColumnData::Utf8 {
71					..
72				},
73				other,
74			) => Err(ScalarFunctionError::InvalidArgumentType {
75				function: ctx.fragment.clone(),
76				argument_index: 1,
77				expected: vec![Type::Utf8],
78				actual: other.get_type(),
79			}),
80			(other, _) => Err(ScalarFunctionError::InvalidArgumentType {
81				function: ctx.fragment.clone(),
82				argument_index: 0,
83				expected: vec![Type::Utf8],
84				actual: other.get_type(),
85			}),
86		}
87	}
88
89	fn return_type(&self, _input_types: &[Type]) -> Type {
90		Type::Boolean
91	}
92}