datafusion_functions/string/
ends_with.rs1use std::any::Any;
19use std::sync::Arc;
20
21use arrow::array::ArrayRef;
22use arrow::datatypes::DataType;
23
24use crate::utils::make_scalar_function;
25use datafusion_common::{internal_err, Result};
26use datafusion_expr::{ColumnarValue, Documentation, Volatility};
27use datafusion_expr::{ScalarFunctionArgs, ScalarUDFImpl, Signature};
28use datafusion_macros::user_doc;
29
30#[user_doc(
31 doc_section(label = "String Functions"),
32 description = "Tests if a string ends with a substring.",
33 syntax_example = "ends_with(str, substr)",
34 sql_example = r#"```sql
35> select ends_with('datafusion', 'soin');
36+--------------------------------------------+
37| ends_with(Utf8("datafusion"),Utf8("soin")) |
38+--------------------------------------------+
39| false |
40+--------------------------------------------+
41> select ends_with('datafusion', 'sion');
42+--------------------------------------------+
43| ends_with(Utf8("datafusion"),Utf8("sion")) |
44+--------------------------------------------+
45| true |
46+--------------------------------------------+
47```"#,
48 standard_argument(name = "str", prefix = "String"),
49 argument(name = "substr", description = "Substring to test for.")
50)]
51#[derive(Debug)]
52pub struct EndsWithFunc {
53 signature: Signature,
54}
55
56impl Default for EndsWithFunc {
57 fn default() -> Self {
58 EndsWithFunc::new()
59 }
60}
61
62impl EndsWithFunc {
63 pub fn new() -> Self {
64 Self {
65 signature: Signature::string(2, Volatility::Immutable),
66 }
67 }
68}
69
70impl ScalarUDFImpl for EndsWithFunc {
71 fn as_any(&self) -> &dyn Any {
72 self
73 }
74
75 fn name(&self) -> &str {
76 "ends_with"
77 }
78
79 fn signature(&self) -> &Signature {
80 &self.signature
81 }
82
83 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
84 Ok(DataType::Boolean)
85 }
86
87 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
88 match args.args[0].data_type() {
89 DataType::Utf8View | DataType::Utf8 | DataType::LargeUtf8 => {
90 make_scalar_function(ends_with, vec![])(&args.args)
91 }
92 other => {
93 internal_err!("Unsupported data type {other:?} for function ends_with. Expected Utf8, LargeUtf8 or Utf8View")?
94 }
95 }
96 }
97
98 fn documentation(&self) -> Option<&Documentation> {
99 self.doc()
100 }
101}
102
103pub fn ends_with(args: &[ArrayRef]) -> Result<ArrayRef> {
106 let result = arrow::compute::kernels::comparison::ends_with(&args[0], &args[1])?;
107
108 Ok(Arc::new(result) as ArrayRef)
109}
110
111#[cfg(test)]
112mod tests {
113 use arrow::array::{Array, BooleanArray};
114 use arrow::datatypes::DataType::Boolean;
115
116 use datafusion_common::Result;
117 use datafusion_common::ScalarValue;
118 use datafusion_expr::{ColumnarValue, ScalarUDFImpl};
119
120 use crate::string::ends_with::EndsWithFunc;
121 use crate::utils::test::test_function;
122
123 #[test]
124 fn test_functions() -> Result<()> {
125 test_function!(
126 EndsWithFunc::new(),
127 vec![
128 ColumnarValue::Scalar(ScalarValue::from("alphabet")),
129 ColumnarValue::Scalar(ScalarValue::from("alph")),
130 ],
131 Ok(Some(false)),
132 bool,
133 Boolean,
134 BooleanArray
135 );
136 test_function!(
137 EndsWithFunc::new(),
138 vec![
139 ColumnarValue::Scalar(ScalarValue::from("alphabet")),
140 ColumnarValue::Scalar(ScalarValue::from("bet")),
141 ],
142 Ok(Some(true)),
143 bool,
144 Boolean,
145 BooleanArray
146 );
147 test_function!(
148 EndsWithFunc::new(),
149 vec![
150 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
151 ColumnarValue::Scalar(ScalarValue::from("alph")),
152 ],
153 Ok(None),
154 bool,
155 Boolean,
156 BooleanArray
157 );
158 test_function!(
159 EndsWithFunc::new(),
160 vec![
161 ColumnarValue::Scalar(ScalarValue::from("alphabet")),
162 ColumnarValue::Scalar(ScalarValue::Utf8(None)),
163 ],
164 Ok(None),
165 bool,
166 Boolean,
167 BooleanArray
168 );
169
170 Ok(())
171 }
172}