risingwave_expr_impl/scalar/
array_flatten.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
15use risingwave_common::array::{ListRef, ListValue};
16use risingwave_expr::expr::Context;
17use risingwave_expr::{ExprError, Result, function};
18
19/// Flattens a nested array by concatenating the inner arrays into a single array.
20/// Only the outermost level of nesting is removed. For deeper nested arrays, call
21/// `array_flatten` multiple times.
22///
23/// Examples:
24///
25/// ```slt
26/// query T
27/// select array_flatten(array[array[1, 2], array[3, 4]]);
28/// ----
29/// {1,2,3,4}
30///
31/// query T
32/// select array_flatten(array[array[1, 2], array[]::int[], array[3, 4]]);
33/// ----
34/// {1,2,3,4}
35///
36/// query T
37/// select array_flatten(array[array[1, 2], null, array[3, 4]]);
38/// ----
39/// {1,2,3,4}
40///
41/// query T
42/// select array_flatten(array[array[array[1], array[2, null]], array[array[3, 4], null::int[]]]);
43/// ----
44/// {{1},{2,NULL},{3,4},NULL}
45///
46/// query T
47/// select array_flatten(array[[]]::int[][]);
48/// ----
49/// {}
50///
51/// query T
52/// select array_flatten(array[[null, 1]]::int[][]);
53/// ----
54/// {NULL,1}
55///
56/// query T
57/// select array_flatten(array[]::int[][]);
58/// ----
59/// {}
60///
61/// query T
62/// select array_flatten(null::int[][]);
63/// ----
64/// NULL
65/// ```
66#[function("array_flatten(anyarray) -> anyarray")]
67fn array_flatten(array: ListRef<'_>, ctx: &Context) -> Result<ListValue> {
68    // The elements of the array must be arrays themselves
69    let outer_type = &ctx.arg_types[0];
70    let inner_type = if outer_type.is_array() {
71        outer_type.as_list_element_type()
72    } else {
73        return Err(ExprError::InvalidParam {
74            name: "array_flatten",
75            reason: Box::from("expected the argument to be an array of arrays"),
76        });
77    };
78    if !inner_type.is_array() {
79        return Err(ExprError::InvalidParam {
80            name: "array_flatten",
81            reason: Box::from("expected the argument to be an array of arrays"),
82        });
83    }
84    let inner_elem_type = inner_type.as_list_element_type();
85
86    // Collect all inner array elements and flatten them into a single array
87    Ok(ListValue::from_datum_iter(
88        inner_elem_type,
89        array
90            .iter()
91            // Filter out NULL inner arrays
92            .flatten()
93            // Flatten all inner arrays
94            .flat_map(|inner_array| inner_array.into_list().iter()),
95    ))
96}