Skip to main content

datafusion_functions_nested/
repeat.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! [`ScalarUDFImpl`] definitions for array_repeat function.
19
20use crate::utils::make_scalar_function;
21use arrow::array::{
22    Array, ArrayRef, BooleanBufferBuilder, GenericListArray, Int64Array, OffsetSizeTrait,
23    UInt64Array,
24};
25use arrow::buffer::{NullBuffer, OffsetBuffer};
26use arrow::compute;
27use arrow::datatypes::DataType;
28use arrow::datatypes::{
29    DataType::{LargeList, List},
30    Field,
31};
32use datafusion_common::cast::{as_int64_array, as_large_list_array, as_list_array};
33use datafusion_common::types::{NativeType, logical_int64};
34use datafusion_common::{Result, exec_datafusion_err};
35use datafusion_expr::{
36    ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
37    Volatility,
38};
39use datafusion_expr_common::signature::{Coercion, TypeSignatureClass};
40use datafusion_macros::user_doc;
41use std::mem::size_of;
42use std::sync::Arc;
43
44const ARRAY_REPEAT_LENGTH_EXCEEDED: &str =
45    "array_repeat: requested length exceeds maximum array size";
46
47make_udf_expr_and_func!(
48    ArrayRepeat,
49    array_repeat,
50    element count, // arg name
51    "returns an array containing element `count` times.", // doc
52    array_repeat_udf // internal function name
53);
54
55#[user_doc(
56    doc_section(label = "Array Functions"),
57    description = "Returns an array containing element `count` times.",
58    syntax_example = "array_repeat(element, count)",
59    sql_example = r#"```sql
60> select array_repeat(1, 3);
61+---------------------------------+
62| array_repeat(Int64(1),Int64(3)) |
63+---------------------------------+
64| [1, 1, 1]                       |
65+---------------------------------+
66> select array_repeat([1, 2], 2);
67+------------------------------------+
68| array_repeat(List([1,2]),Int64(2)) |
69+------------------------------------+
70| [[1, 2], [1, 2]]                   |
71+------------------------------------+
72```"#,
73    argument(
74        name = "element",
75        description = "Element expression. Can be a constant, column, or function, and any combination of array operators."
76    ),
77    argument(
78        name = "count",
79        description = "Value of how many times to repeat the element."
80    )
81)]
82#[derive(Debug, PartialEq, Eq, Hash)]
83pub struct ArrayRepeat {
84    signature: Signature,
85    aliases: Vec<String>,
86}
87
88impl Default for ArrayRepeat {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl ArrayRepeat {
95    pub fn new() -> Self {
96        Self {
97            signature: Signature::coercible(
98                vec![
99                    Coercion::new_exact(TypeSignatureClass::Any),
100                    Coercion::new_implicit(
101                        TypeSignatureClass::Native(logical_int64()),
102                        vec![TypeSignatureClass::Integer],
103                        NativeType::Int64,
104                    ),
105                ],
106                Volatility::Immutable,
107            ),
108            aliases: vec![String::from("list_repeat")],
109        }
110    }
111}
112
113impl ScalarUDFImpl for ArrayRepeat {
114    fn name(&self) -> &str {
115        "array_repeat"
116    }
117
118    fn signature(&self) -> &Signature {
119        &self.signature
120    }
121
122    fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
123        let element_type = &arg_types[0];
124        match element_type {
125            LargeList(_) => Ok(LargeList(Arc::new(Field::new_list_field(
126                element_type.clone(),
127                true,
128            )))),
129            _ => Ok(List(Arc::new(Field::new_list_field(
130                element_type.clone(),
131                true,
132            )))),
133        }
134    }
135
136    fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
137        make_scalar_function(array_repeat_inner)(&args.args)
138    }
139
140    fn aliases(&self) -> &[String] {
141        &self.aliases
142    }
143
144    fn documentation(&self) -> Option<&Documentation> {
145        self.doc()
146    }
147}
148
149fn array_repeat_inner(args: &[ArrayRef]) -> Result<ArrayRef> {
150    let element = &args[0];
151    let count_array = as_int64_array(&args[1])?;
152
153    match element.data_type() {
154        List(_) => {
155            let list_array = as_list_array(element)?;
156            general_list_repeat::<i32>(list_array, count_array)
157        }
158        LargeList(_) => {
159            let list_array = as_large_list_array(element)?;
160            general_list_repeat::<i64>(list_array, count_array)
161        }
162        _ => general_repeat::<i32>(element, count_array),
163    }
164}
165
166/// For each element of `array[i]` repeat `count_array[i]` times.
167///
168/// Assumption for the input:
169///     1. `count[i] >= 0`
170///     2. `array.len() == count_array.len()`
171///
172/// For example,
173/// ```text
174/// array_repeat(
175///     [1, 2, 3], [2, 0, 1] => [[1, 1], [], [3]]
176/// )
177/// ```
178fn general_repeat<O: OffsetSizeTrait>(
179    array: &ArrayRef,
180    count_array: &Int64Array,
181) -> Result<ArrayRef> {
182    let total_repeated_values =
183        (0..count_array.len()).try_fold(0usize, |total, idx| {
184            total
185                .checked_add(repeat_count(count_array, idx).unwrap_or_default())
186                .ok_or_else(|| {
187                    exec_datafusion_err!(
188                        "array_repeat: total repeated values overflowed usize"
189                    )
190                })
191        })?;
192    ensure_repeated_values_fit::<O>(total_repeated_values)?;
193    let (offsets, _) = build_repeat_offsets::<O>(count_array)?;
194
195    let mut take_indices = Vec::with_capacity(total_repeated_values);
196
197    for idx in 0..count_array.len() {
198        let Some(count) = repeat_count(count_array, idx) else {
199            continue;
200        };
201        take_indices.extend(std::iter::repeat_n(idx as u64, count));
202    }
203
204    // Build the flattened values
205    let repeated_values = compute::take(
206        array.as_ref(),
207        &UInt64Array::from_iter_values(take_indices),
208        None,
209    )?;
210
211    // Construct final ListArray
212    Ok(Arc::new(GenericListArray::<O>::try_new(
213        Arc::new(Field::new_list_field(array.data_type().to_owned(), true)),
214        OffsetBuffer::new(offsets.into()),
215        repeated_values,
216        count_array.nulls().cloned(),
217    )?))
218}
219
220/// Handle List version of `general_repeat`
221///
222/// For each element of `list_array[i]` repeat `count_array[i]` times.
223///
224/// For example,
225/// ```text
226/// array_repeat(
227///     [[1, 2, 3], [4, 5], [6]], [2, 0, 1] => [[[1, 2, 3], [1, 2, 3]], [], [[6]]]
228/// )
229/// ```
230fn general_list_repeat<O: OffsetSizeTrait>(
231    list_array: &GenericListArray<O>,
232    count_array: &Int64Array,
233) -> Result<ArrayRef> {
234    let list_offsets = list_array.value_offsets();
235    let (outer_offsets, outer_total) = build_repeat_offsets::<O>(count_array)?;
236
237    // calculate capacities for pre-allocation
238    let mut inner_total = 0usize;
239    for i in 0..count_array.len() {
240        let Some(count) = repeat_count(count_array, i) else {
241            continue;
242        };
243        if count > 0 && list_array.is_valid(i) {
244            let len = list_offsets[i + 1].to_usize().unwrap()
245                - list_offsets[i].to_usize().unwrap();
246            inner_total =
247                checked_repeat_len_add(inner_total, checked_repeat_len_mul(len, count)?)?;
248            ensure_repeated_values_fit::<O>(inner_total)?;
249        }
250    }
251
252    // Build inner structures
253    let inner_offsets_capacity = checked_offset_slots_capacity::<O>(outer_total)?;
254    let mut inner_offsets = Vec::with_capacity(inner_offsets_capacity);
255    let mut take_indices = Vec::with_capacity(inner_total);
256    let mut inner_nulls = BooleanBufferBuilder::new(outer_total);
257    let mut inner_running = 0usize;
258    inner_offsets.push(O::zero());
259
260    for row_idx in 0..count_array.len() {
261        let Some(count) = repeat_count(count_array, row_idx) else {
262            continue;
263        };
264        let list_is_valid = list_array.is_valid(row_idx);
265        let start = list_offsets[row_idx].to_usize().unwrap();
266        let end = list_offsets[row_idx + 1].to_usize().unwrap();
267        let row_len = end - start;
268
269        for _ in 0..count {
270            inner_running = checked_repeat_len_add(inner_running, row_len)?;
271            ensure_repeated_values_fit::<O>(inner_running)?;
272            let offset = checked_repeat_offset::<O>(inner_running)?;
273            inner_offsets.push(offset);
274            inner_nulls.append(list_is_valid);
275            if list_is_valid {
276                take_indices.extend(start as u64..end as u64);
277            }
278        }
279    }
280
281    // Build inner ListArray
282    let inner_values = compute::take(
283        list_array.values().as_ref(),
284        &UInt64Array::from_iter_values(take_indices),
285        None,
286    )?;
287    let inner_list = GenericListArray::<O>::try_new(
288        Arc::new(Field::new_list_field(list_array.value_type().clone(), true)),
289        OffsetBuffer::new(inner_offsets.into()),
290        inner_values,
291        Some(NullBuffer::new(inner_nulls.finish())),
292    )?;
293
294    Ok(Arc::new(GenericListArray::<O>::try_new(
295        Arc::new(Field::new_list_field(
296            list_array.data_type().to_owned(),
297            true,
298        )),
299        OffsetBuffer::new(outer_offsets.into()),
300        Arc::new(inner_list),
301        count_array.nulls().cloned(),
302    )?))
303}
304
305fn build_repeat_offsets<O: OffsetSizeTrait>(
306    count_array: &Int64Array,
307) -> Result<(Vec<O>, usize)> {
308    let offsets_capacity = checked_offset_slots_capacity::<O>(count_array.len())?;
309    let mut offsets = Vec::with_capacity(offsets_capacity);
310    offsets.push(O::zero());
311    let mut running_offset = 0usize;
312
313    for idx in 0..count_array.len() {
314        let Some(count) = repeat_count(count_array, idx) else {
315            offsets.push(*offsets.last().unwrap());
316            continue;
317        };
318        running_offset = checked_repeat_len_add(running_offset, count)?;
319        ensure_repeated_values_fit::<O>(running_offset)?;
320        let offset = checked_repeat_offset::<O>(running_offset)?;
321        offsets.push(offset);
322    }
323
324    Ok((offsets, running_offset))
325}
326
327fn checked_repeat_len_add(lhs: usize, rhs: usize) -> Result<usize> {
328    lhs.checked_add(rhs)
329        .ok_or_else(|| exec_datafusion_err!("{}", ARRAY_REPEAT_LENGTH_EXCEEDED))
330}
331
332fn checked_repeat_len_mul(lhs: usize, rhs: usize) -> Result<usize> {
333    lhs.checked_mul(rhs)
334        .ok_or_else(|| exec_datafusion_err!("{}", ARRAY_REPEAT_LENGTH_EXCEEDED))
335}
336
337fn ensure_repeated_values_fit<O: OffsetSizeTrait>(len: usize) -> Result<()> {
338    ensure_vec_capacity::<u64>(len)?;
339    checked_repeat_offset::<O>(len)?;
340
341    Ok(())
342}
343
344fn ensure_vec_capacity<T>(len: usize) -> Result<()> {
345    if len > max_vec_elements::<T>() {
346        return Err(exec_datafusion_err!("{}", ARRAY_REPEAT_LENGTH_EXCEEDED));
347    }
348
349    Ok(())
350}
351
352fn checked_offset_slots_capacity<O>(len: usize) -> Result<usize> {
353    let capacity = checked_repeat_len_add(len, 1)?;
354    ensure_vec_capacity::<O>(capacity)?;
355
356    Ok(capacity)
357}
358
359fn checked_repeat_offset<O: OffsetSizeTrait>(offset: usize) -> Result<O> {
360    O::from_usize(offset).ok_or_else(|| {
361        exec_datafusion_err!(
362            "array_repeat: offset {offset} exceeds the maximum value for offset type"
363        )
364    })
365}
366
367fn max_vec_elements<T>() -> usize {
368    let element_size = size_of::<T>();
369    (isize::MAX as usize)
370        .checked_div(element_size)
371        .unwrap_or(usize::MAX)
372}
373
374/// Helper function to get count from count_array at given index.
375/// Returns `None` for NULL values and `Some(0)` for non-positive counts.
376#[inline]
377fn repeat_count(count_array: &Int64Array, idx: usize) -> Option<usize> {
378    if count_array.is_null(idx) {
379        None
380    } else {
381        let c = count_array.value(idx);
382        Some(if c > 0 { c as usize } else { 0 })
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use super::{array_repeat_inner, general_list_repeat, general_repeat};
389    use arrow::array::{Array, ArrayRef, AsArray, Int32Array, Int64Array, ListArray};
390    use arrow::buffer::{NullBuffer, OffsetBuffer, ScalarBuffer};
391    use arrow::datatypes::{Field, Int32Type};
392    use datafusion_common::Result;
393    use std::sync::Arc;
394
395    #[test]
396    fn test_array_repeat_null_count_stays_null() -> Result<()> {
397        let array: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
398        let counts = Int64Array::new(
399            ScalarBuffer::from(vec![2, 1, 1]),
400            Some(NullBuffer::from(vec![true, false, true])),
401        );
402
403        let result = general_repeat::<i32>(&array, &counts)?;
404        let expected = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
405            Some(vec![Some(1), Some(1)]),
406            None,
407            Some(vec![Some(3)]),
408        ]);
409
410        assert_eq!(result.as_list::<i32>(), &expected);
411
412        Ok(())
413    }
414
415    #[test]
416    fn test_array_repeat_nested_null_count_stays_null() -> Result<()> {
417        let list_array = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
418            Some(vec![Some(1), Some(2)]),
419            Some(vec![Some(3), Some(4)]),
420            Some(vec![Some(5)]),
421        ]);
422        let counts = Int64Array::new(
423            ScalarBuffer::from(vec![2, 1, 1]),
424            Some(NullBuffer::from(vec![true, false, true])),
425        );
426
427        let result = general_list_repeat::<i32>(&list_array, &counts)?;
428        let repeated_values = ListArray::from_iter_primitive::<Int32Type, _, _>(vec![
429            Some(vec![Some(1), Some(2)]),
430            Some(vec![Some(1), Some(2)]),
431            Some(vec![Some(5)]),
432        ]);
433        let expected = ListArray::new(
434            Arc::new(Field::new_list_field(
435                repeated_values.data_type().clone(),
436                true,
437            )),
438            OffsetBuffer::new(ScalarBuffer::from(vec![0, 2, 2, 3])),
439            Arc::new(repeated_values),
440            Some(NullBuffer::from(vec![true, false, true])),
441        );
442
443        assert_eq!(result.as_list::<i32>(), &expected);
444
445        Ok(())
446    }
447
448    #[test]
449    fn scalar_count_exceeding_max_array_size_returns_error() {
450        let element: ArrayRef = Arc::new(Int64Array::from(vec![1]));
451        let count: ArrayRef = Arc::new(Int64Array::from(vec![i64::MAX]));
452
453        let err = array_repeat_inner(&[element, count]).unwrap_err();
454        assert!(
455            err.to_string().starts_with(
456                "Execution error: array_repeat: requested length exceeds maximum array size"
457            ),
458            "unexpected error: {err}"
459        );
460    }
461
462    #[test]
463    fn scalar_count_exceeding_list_offset_limit_returns_error() {
464        let element: ArrayRef = Arc::new(Int64Array::from(vec![1]));
465        let count: ArrayRef = Arc::new(Int64Array::from(vec![i32::MAX as i64 + 1]));
466
467        let err = array_repeat_inner(&[element, count]).unwrap_err();
468        assert!(
469            err.to_string().starts_with(
470                "Execution error: array_repeat: offset 2147483648 exceeds the maximum value for offset type"
471            ),
472            "unexpected error: {err}"
473        );
474    }
475}