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