risingwave_expr_impl/scalar/
array_length.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 std::fmt::Write;
16
17use risingwave_common::array::ListRef;
18use risingwave_expr::{ExprError, Result, function};
19
20/// Returns the length of an array.
21///
22/// ```sql
23/// array_length (array anyarray) → int8
24/// ```
25///
26/// Examples:
27///
28/// ```slt
29/// query T
30/// select array_length(null::int[]);
31/// ----
32/// NULL
33///
34/// query T
35/// select array_length(array[1,2,3]);
36/// ----
37/// 3
38///
39/// query T
40/// select array_length(array[1,2,3,4,1]);
41/// ----
42/// 5
43///
44/// query T
45/// select array_length(array[]::int[]);
46/// ----
47/// 0
48///
49/// query T
50/// select array_length(array[array[1, 2, 3]]);
51/// ----
52/// 1
53///
54/// query T
55/// select array_length(array[NULL]);
56/// ----
57/// 1
58///
59/// query error Cannot implicitly cast
60/// select array_length(null);
61/// ```
62#[function("array_length(anyarray) -> int4")]
63#[function("array_length(anyarray) -> int8", deprecated)]
64fn array_length<T: TryFrom<usize>>(array: ListRef<'_>) -> Result<T> {
65    array
66        .len()
67        .try_into()
68        .map_err(|_| ExprError::NumericOverflow)
69}
70
71/// Returns the length of the requested array dimension.
72///
73/// Examples:
74///
75/// ```slt
76/// query I
77/// select array_length(array[2,3,4], 1);
78/// ----
79/// 3
80///
81/// query I
82/// select array_length(array[2,3,4], 0);
83/// ----
84/// NULL
85///
86/// query I
87/// select array_length(array[2,3,4], -1);
88/// ----
89/// NULL
90///
91/// query I
92/// select array_length(array[2,3,4], null);
93/// ----
94/// NULL
95///
96/// query I
97/// select array_length(array[array[2,3,4],array[3,4,5]], '1');
98/// ----
99/// 2
100///
101/// statement error
102/// select array_length(array[2,3,4], true);
103///
104/// # This one could be supported later, but at the cost of checking all subarrays, to reject the next.
105/// statement error
106/// select array_length(array[array[2,3,4],array[3,4,5]], 2);
107///
108/// statement error
109/// select array_length(array[array[2,3],array[3,4,5]], 2);
110///
111/// # Different from PostgreSQL who treats empty `array[]` as zero dimension and returns NULL.
112/// query I
113/// select array_length(array[]::int[], 1);
114/// ----
115/// 0
116///
117/// query I
118/// select array_length(array[]::int[][], 1);
119/// ----
120/// 0
121///
122/// # This should be NULL but it is hard to access `DataType` in current expression framework.
123/// # The next should remain rejected.
124/// statement error
125/// select array_length(array[1,2,3], 2);
126///
127/// statement error
128/// select array_length(array[null, array[2]], 2);
129/// ```
130#[function("array_length(anyarray, int4) -> int4")]
131fn array_length_of_dim(array: ListRef<'_>, d: i32) -> Result<Option<i32>> {
132    match d {
133        ..=0 => Ok(None),
134        1 => array_length(array).map(Some),
135        2.. => Err(ExprError::InvalidParam {
136            name: "dimension",
137            reason: "array_length for dimensions greater than 1 not supported".into(),
138        }),
139    }
140}
141
142/// Returns a text representation of the array's dimensions.
143///
144/// Examples:
145///
146/// ```slt
147/// query T
148/// select array_dims(array[2,3,4]);
149/// ----
150/// [1:3]
151///
152/// query T
153/// select array_dims(null::int[]);
154/// ----
155/// NULL
156///
157/// query T
158/// select array_dims('{2,3,4}'::int[]);
159/// ----
160/// [1:3]
161///
162/// statement error
163/// select array_dims(null);
164///
165/// statement error
166/// select array_dims(1);
167///
168/// # Similar to `array_length`, higher dimension is rejected now but can be supported later in limited cases.
169/// statement error
170/// select array_dims(array[array[2,3,4],array[3,4,5]]);
171///
172/// statement error
173/// select array_dims(array[array[2,3],array[3,4,5]]);
174///
175/// # And empty array is also different from PostgreSQL, following the same convention as `array_length`.
176/// query T
177/// select array_dims(array[]::int[]);
178/// ----
179/// [1:0]
180///
181/// statement error
182/// select array_dims(array[]::int[][]); -- would be `[1:0][1:0]` after multidimensional support
183///
184/// statement error
185/// select array_dims(array[array[]::int[]]); -- would be `[1:1][1:0]` after multidimensional support
186/// ```
187#[function("array_dims(anyarray) -> varchar")]
188fn array_dims(array: ListRef<'_>, writer: &mut impl Write) {
189    write!(writer, "[1:{}]", array.len()).unwrap();
190}