risingwave_expr_impl/scalar/array_to_string.rs
1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![allow(clippy::unit_arg)]
16
17use risingwave_common::array::*;
18use risingwave_common::types::ToText;
19use risingwave_expr::expr::Context;
20use risingwave_expr::function;
21
22/// Converts each array element to its text representation, and concatenates those
23/// separated by the delimiter string. If `null_string` is given and is not NULL,
24/// then NULL array entries are represented by that string; otherwise, they are omitted.
25///
26/// ```sql
27/// array_to_string ( array anyarray, delimiter text [, null_string text ] ) → text
28/// ```
29///
30/// Examples:
31///
32/// ```slt
33/// query T
34/// select array_to_string(array[1, 2, 3, NULL, 5], ',')
35/// ----
36/// 1,2,3,5
37///
38/// query T
39/// select array_to_string(array[1, 2, 3, NULL, 5], ',', '*')
40/// ----
41/// 1,2,3,*,5
42///
43/// query T
44/// select array_to_string(array[null,'foo',null], ',', '*');
45/// ----
46/// *,foo,*
47///
48/// query T
49/// select array_to_string(array['2023-02-20 17:35:25'::timestamp, null,'2023-02-19 13:01:30'::timestamp], ',', '*');
50/// ----
51/// 2023-02-20 17:35:25,*,2023-02-19 13:01:30
52///
53/// query T
54/// with t as (
55/// select array[1,null,2,3] as arr, ',' as d union all
56/// select array[4,5,6,null,7] as arr, '|')
57/// select array_to_string(arr, d) from t;
58/// ----
59/// 1,2,3
60/// 4|5|6|7
61///
62/// # `array` or `delimiter` are required. Otherwise, returns null.
63/// query T
64/// select array_to_string(array[1,2], NULL);
65/// ----
66/// NULL
67///
68/// query error polymorphic type
69/// select array_to_string(null, ',');
70///
71/// # multidimensional array
72/// query T
73/// select array_to_string(array[array['one', null], array['three', 'four']]::text[][], ',');
74/// ----
75/// one,three,four
76///
77/// query T
78/// select array_to_string(array[array['one', null], array['three', 'four']]::text[][], ',', '*');
79/// ----
80/// one,*,three,four
81/// ```
82#[function("array_to_string(anyarray, varchar) -> varchar")]
83fn array_to_string(
84 array: ListRef<'_>,
85 delimiter: &str,
86 ctx: &Context,
87 writer: &mut impl std::fmt::Write,
88) {
89 let element_data_type = ctx.arg_types[0].unnest_list();
90 let mut first = true;
91 for element in array.flatten().iter() {
92 let Some(element) = element else { continue };
93 if !first {
94 write!(writer, "{}", delimiter).unwrap();
95 } else {
96 first = false;
97 }
98 element.write_with_type(element_data_type, writer).unwrap();
99 }
100}
101
102#[function("array_to_string(anyarray, varchar, varchar) -> varchar")]
103fn array_to_string_with_null(
104 array: ListRef<'_>,
105 delimiter: &str,
106 null_string: &str,
107 ctx: &Context,
108 writer: &mut impl std::fmt::Write,
109) {
110 let element_data_type = ctx.arg_types[0].unnest_list();
111 let mut first = true;
112 for element in array.flatten().iter() {
113 if !first {
114 write!(writer, "{}", delimiter).unwrap();
115 } else {
116 first = false;
117 }
118 match element {
119 Some(s) => s.write_with_type(element_data_type, writer).unwrap(),
120 None => write!(writer, "{}", null_string).unwrap(),
121 }
122 }
123}