Skip to main content

datafusion_expr_common/
columnar_value.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//! [`ColumnarValue`] represents the result of evaluating an expression.
19
20use arrow::{
21    array::{
22        Array, ArrayRef, Date32Array, Date64Array, NullArray, TimestampMicrosecondArray,
23        TimestampMillisecondArray, TimestampNanosecondArray, TimestampSecondArray,
24    },
25    compute::{CastOptions, kernels, max, min},
26    datatypes::{DataType, TimeUnit},
27    util::pretty::pretty_format_columns,
28};
29use datafusion_common::internal_datafusion_err;
30use datafusion_common::{
31    Result, ScalarValue,
32    format::DEFAULT_CAST_OPTIONS,
33    internal_err,
34    scalar::{
35        date_to_timestamp_multiplier, ensure_timestamp_in_bounds,
36        timestamp_to_timestamp_multiplier,
37    },
38};
39use std::fmt;
40use std::sync::Arc;
41
42/// The result of evaluating an expression.
43///
44/// [`ColumnarValue::Scalar`] represents a single value repeated any number of
45/// times. This is an important performance optimization for handling values
46/// that do not change across rows.
47///
48/// [`ColumnarValue::Array`] represents a column of data, stored as an  Arrow
49/// [`ArrayRef`]
50///
51/// A slice of `ColumnarValue`s logically represents a table, with each column
52/// having the same number of rows. This means that all `Array`s are the same
53/// length.
54///
55/// # Example
56///
57/// A `ColumnarValue::Array` with an array of 5 elements and a
58/// `ColumnarValue::Scalar` with the value 100
59///
60/// ```text
61/// ┌──────────────┐
62/// │ ┌──────────┐ │
63/// │ │   "A"    │ │
64/// │ ├──────────┤ │
65/// │ │   "B"    │ │
66/// │ ├──────────┤ │
67/// │ │   "C"    │ │
68/// │ ├──────────┤ │
69/// │ │   "D"    │ │        ┌──────────────┐
70/// │ ├──────────┤ │        │ ┌──────────┐ │
71/// │ │   "E"    │ │        │ │   100    │ │
72/// │ └──────────┘ │        │ └──────────┘ │
73/// └──────────────┘        └──────────────┘
74///
75///  ColumnarValue::        ColumnarValue::
76///       Array                 Scalar
77/// ```
78///
79/// Logically represents the following table:
80///
81/// | Column 1| Column 2 |
82/// | ------- | -------- |
83/// | A | 100 |
84/// | B | 100 |
85/// | C | 100 |
86/// | D | 100 |
87/// | E | 100 |
88///
89/// # Performance Notes
90///
91/// When implementing functions or operators, it is important to consider the
92/// performance implications of handling scalar values.
93///
94/// Because all functions must handle [`ArrayRef`], it is
95/// convenient to convert [`ColumnarValue::Scalar`]s using
96/// [`Self::into_array`]. For example,  [`ColumnarValue::values_to_arrays`]
97/// converts multiple columnar values into arrays of the same length.
98///
99/// However, it is often much more performant to provide a different,
100/// implementation that handles scalar values differently
101#[derive(Clone, Debug)]
102pub enum ColumnarValue {
103    /// Array of values
104    Array(ArrayRef),
105    /// A single value
106    Scalar(ScalarValue),
107}
108
109impl From<ArrayRef> for ColumnarValue {
110    fn from(value: ArrayRef) -> Self {
111        ColumnarValue::Array(value)
112    }
113}
114
115impl From<ScalarValue> for ColumnarValue {
116    fn from(value: ScalarValue) -> Self {
117        ColumnarValue::Scalar(value)
118    }
119}
120
121impl ColumnarValue {
122    pub fn data_type(&self) -> DataType {
123        match self {
124            ColumnarValue::Array(array_value) => array_value.data_type().clone(),
125            ColumnarValue::Scalar(scalar_value) => scalar_value.data_type(),
126        }
127    }
128
129    /// Convert any [`Self::Scalar`] into an Arrow [`ArrayRef`] with the specified
130    /// number of rows  by repeating the same scalar multiple times,
131    /// which is not as efficient as handling the scalar directly.
132    /// [`Self::Array`] will just be returned as is.
133    ///
134    /// See [`Self::into_array_of_size`] if you need to validate the length of the output array.
135    ///
136    /// See [`Self::values_to_arrays`] to convert multiple columnar values into
137    /// arrays of the same length.
138    ///
139    /// # Errors
140    ///
141    /// Errors if `self` is a Scalar that fails to be converted into an array of size
142    pub fn into_array(self, num_rows: usize) -> Result<ArrayRef> {
143        Ok(match self {
144            ColumnarValue::Array(array) => array,
145            ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(num_rows)?,
146        })
147    }
148
149    /// Convert a columnar value into an Arrow [`ArrayRef`] with the specified
150    /// number of rows. [`Self::Scalar`] is converted by repeating the same
151    /// scalar multiple times which is not as efficient as handling the scalar
152    /// directly.
153    /// This validates that if this is [`Self::Array`], it has the expected length.
154    ///
155    /// See [`Self::values_to_arrays`] to convert multiple columnar values into
156    /// arrays of the same length.
157    ///
158    /// # Errors
159    ///
160    /// Errors if `self` is a Scalar that fails to be converted into an array of size or
161    /// if the array length does not match the expected length
162    pub fn into_array_of_size(self, num_rows: usize) -> Result<ArrayRef> {
163        match self {
164            ColumnarValue::Array(array) => {
165                if array.len() == num_rows {
166                    Ok(array)
167                } else {
168                    internal_err!(
169                        "Array length {} does not match expected length {}",
170                        array.len(),
171                        num_rows
172                    )
173                }
174            }
175            ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(num_rows),
176        }
177    }
178
179    /// Convert any [`Self::Scalar`] into an Arrow [`ArrayRef`] with the specified
180    /// number of rows  by repeating the same scalar multiple times,
181    /// which is not as efficient as handling the scalar directly.
182    /// [`Self::Array`] will just be returned as is.
183    ///
184    /// See [`Self::to_array_of_size`] if you need to validate the length of the output array.
185    ///
186    /// See [`Self::values_to_arrays`] to convert multiple columnar values into
187    /// arrays of the same length.
188    ///
189    /// # Errors
190    ///
191    /// Errors if `self` is a Scalar that fails to be converted into an array of size
192    pub fn to_array(&self, num_rows: usize) -> Result<ArrayRef> {
193        Ok(match self {
194            ColumnarValue::Array(array) => Arc::clone(array),
195            ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(num_rows)?,
196        })
197    }
198
199    /// Convert a columnar value into an Arrow [`ArrayRef`] with the specified
200    /// number of rows. [`Self::Scalar`] is converted by repeating the same
201    /// scalar multiple times which is not as efficient as handling the scalar
202    /// directly.
203    /// This validates that if this is [`Self::Array`], it has the expected length.
204    ///
205    /// See [`Self::values_to_arrays`] to convert multiple columnar values into
206    /// arrays of the same length.
207    ///
208    /// # Errors
209    ///
210    /// Errors if `self` is a Scalar that fails to be converted into an array of size or
211    /// if the array length does not match the expected length
212    pub fn to_array_of_size(&self, num_rows: usize) -> Result<ArrayRef> {
213        match self {
214            ColumnarValue::Array(array) => {
215                if array.len() == num_rows {
216                    Ok(Arc::clone(array))
217                } else {
218                    internal_err!(
219                        "Array length {} does not match expected length {}",
220                        array.len(),
221                        num_rows
222                    )
223                }
224            }
225            ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(num_rows),
226        }
227    }
228
229    /// Null columnar values are implemented as a null array in order to pass batch
230    /// num_rows
231    pub fn create_null_array(num_rows: usize) -> Self {
232        ColumnarValue::Array(Arc::new(NullArray::new(num_rows)))
233    }
234
235    /// Converts  [`ColumnarValue`]s to [`ArrayRef`]s with the same length.
236    ///
237    /// # Performance Note
238    ///
239    /// This function expands any [`ScalarValue`] to an array. This expansion
240    /// permits using a single function in terms of arrays, but it can be
241    /// inefficient compared to handling the scalar value directly.
242    ///
243    /// Thus, it is recommended to provide specialized implementations for
244    /// scalar values if performance is a concern.
245    ///
246    /// # Errors
247    ///
248    /// If there are multiple array arguments that have different lengths
249    pub fn values_to_arrays(args: &[ColumnarValue]) -> Result<Vec<ArrayRef>> {
250        if args.is_empty() {
251            return Ok(vec![]);
252        }
253
254        let mut array_len = None;
255        for arg in args {
256            array_len = match (arg, array_len) {
257                (ColumnarValue::Array(a), None) => Some(a.len()),
258                (ColumnarValue::Array(a), Some(array_len)) => {
259                    if array_len == a.len() {
260                        Some(array_len)
261                    } else {
262                        return internal_err!(
263                            "Arguments has mixed length. Expected length: {array_len}, found length: {}",
264                            a.len()
265                        );
266                    }
267                }
268                (ColumnarValue::Scalar(_), array_len) => array_len,
269            }
270        }
271
272        // If array_len is none, it means there are only scalars, so make a 1 element array
273        let inferred_length = array_len.unwrap_or(1);
274
275        let args = args
276            .iter()
277            .map(|arg| arg.to_array(inferred_length))
278            .collect::<Result<Vec<_>>>()?;
279
280        Ok(args)
281    }
282
283    /// Cast this [ColumnarValue] to the specified `DataType`
284    ///
285    /// # Struct Casting Behavior
286    ///
287    /// When casting struct types, fields are matched **by name** rather than position:
288    /// - Source fields are matched to target fields using case-sensitive name comparison
289    /// - Fields are reordered to match the target schema
290    /// - Missing target fields are filled with null arrays
291    /// - Extra source fields are ignored
292    ///
293    /// For non-struct types, uses Arrow's standard positional casting.
294    pub fn cast_to(
295        &self,
296        cast_type: &DataType,
297        cast_options: Option<&CastOptions<'static>>,
298    ) -> Result<ColumnarValue> {
299        let cast_options = cast_options.cloned().unwrap_or(DEFAULT_CAST_OPTIONS);
300        match self {
301            ColumnarValue::Array(array) => {
302                let casted = cast_array_by_name(array, cast_type, &cast_options)?;
303                Ok(ColumnarValue::Array(casted))
304            }
305            ColumnarValue::Scalar(scalar) => Ok(ColumnarValue::Scalar(
306                scalar.cast_to_with_options(cast_type, &cast_options)?,
307            )),
308        }
309    }
310}
311
312fn cast_array_by_name(
313    array: &ArrayRef,
314    cast_type: &DataType,
315    cast_options: &CastOptions<'static>,
316) -> Result<ArrayRef> {
317    // If types are already equal, no cast needed
318    if array.data_type() == cast_type {
319        return Ok(Arc::clone(array));
320    }
321
322    if datafusion_common::nested_struct::requires_nested_struct_cast(
323        array.data_type(),
324        cast_type,
325    ) {
326        datafusion_common::nested_struct::cast_column(array, cast_type, cast_options)
327    } else {
328        if !cast_options.safe {
329            ensure_temporal_array_timestamp_bounds(array, cast_type)?;
330        }
331        Ok(kernels::cast::cast_with_options(
332            array,
333            cast_type,
334            cast_options,
335        )?)
336    }
337}
338
339fn ensure_temporal_array_timestamp_bounds(
340    array: &ArrayRef,
341    cast_type: &DataType,
342) -> Result<()> {
343    let source_type = array.data_type().clone();
344    let Some(multiplier) = date_to_timestamp_multiplier(&source_type, cast_type)
345        .or_else(|| timestamp_to_timestamp_multiplier(&source_type, cast_type))
346    else {
347        return Ok(());
348    };
349
350    if multiplier <= 1 {
351        return Ok(());
352    }
353
354    // Use compute kernels to find min/max instead of iterating all elements
355    let (min_val, max_val): (Option<i64>, Option<i64>) = match &source_type {
356        DataType::Date32 => {
357            let arr = array
358                .as_any()
359                .downcast_ref::<Date32Array>()
360                .ok_or_else(|| {
361                    internal_datafusion_err!(
362                        "Expected Date32Array but found {}",
363                        array.data_type()
364                    )
365                })?;
366            (min(arr).map(|v| v as i64), max(arr).map(|v| v as i64))
367        }
368        DataType::Date64 => {
369            let arr = array
370                .as_any()
371                .downcast_ref::<Date64Array>()
372                .ok_or_else(|| {
373                    internal_datafusion_err!(
374                        "Expected Date64Array but found {}",
375                        array.data_type()
376                    )
377                })?;
378            (min(arr), max(arr))
379        }
380        DataType::Timestamp(TimeUnit::Second, _) => {
381            let arr = array
382                .as_any()
383                .downcast_ref::<TimestampSecondArray>()
384                .ok_or_else(|| {
385                    internal_datafusion_err!(
386                        "Expected TimestampSecondArray but found {}",
387                        array.data_type()
388                    )
389                })?;
390            (min(arr), max(arr))
391        }
392        DataType::Timestamp(TimeUnit::Millisecond, _) => {
393            let arr = array
394                .as_any()
395                .downcast_ref::<TimestampMillisecondArray>()
396                .ok_or_else(|| {
397                    internal_datafusion_err!(
398                        "Expected TimestampMillisecondArray but found {}",
399                        array.data_type()
400                    )
401                })?;
402            (min(arr), max(arr))
403        }
404        DataType::Timestamp(TimeUnit::Microsecond, _) => {
405            let arr = array
406                .as_any()
407                .downcast_ref::<TimestampMicrosecondArray>()
408                .ok_or_else(|| {
409                    internal_datafusion_err!(
410                        "Expected TimestampMicrosecondArray but found {}",
411                        array.data_type()
412                    )
413                })?;
414            (min(arr), max(arr))
415        }
416        DataType::Timestamp(TimeUnit::Nanosecond, _) => {
417            let arr = array
418                .as_any()
419                .downcast_ref::<TimestampNanosecondArray>()
420                .ok_or_else(|| {
421                    internal_datafusion_err!(
422                        "Expected TimestampNanosecondArray but found {}",
423                        array.data_type()
424                    )
425                })?;
426            (min(arr), max(arr))
427        }
428        _ => return Ok(()), // Not a temporal type that needs checking.
429    };
430
431    // Only validate the min and max values instead of all elements
432    if let Some(min) = min_val {
433        ensure_timestamp_in_bounds(min, multiplier, &source_type, cast_type)?;
434    }
435    if let Some(max) = max_val {
436        ensure_timestamp_in_bounds(max, multiplier, &source_type, cast_type)?;
437    }
438
439    Ok(())
440}
441
442// Implement Display trait for ColumnarValue
443impl fmt::Display for ColumnarValue {
444    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
445        let formatted = match self {
446            ColumnarValue::Array(array) => {
447                pretty_format_columns("ColumnarValue(ArrayRef)", &[Arc::clone(array)])
448            }
449            ColumnarValue::Scalar(_) => {
450                if let Ok(array) = self.to_array(1) {
451                    pretty_format_columns("ColumnarValue(ScalarValue)", &[array])
452                } else {
453                    return write!(f, "Error formatting columnar value");
454                }
455            }
456        };
457
458        if let Ok(formatted) = formatted {
459            write!(f, "{formatted}")
460        } else {
461            write!(f, "Error formatting columnar value")
462        }
463    }
464}
465
466#[cfg(test)]
467mod tests {
468    use super::*;
469    use arrow::{
470        array::{Date64Array, Int32Array, StructArray},
471        datatypes::{Field, Fields, TimeUnit},
472    };
473
474    #[test]
475    fn into_array_of_size() {
476        // Array case
477        let arr = make_array(1, 3);
478        let arr_columnar_value = ColumnarValue::Array(Arc::clone(&arr));
479        assert_eq!(&arr_columnar_value.into_array_of_size(3).unwrap(), &arr);
480
481        // Scalar case
482        let scalar_columnar_value = ColumnarValue::Scalar(ScalarValue::Int32(Some(42)));
483        let expected_array = make_array(42, 100);
484        assert_eq!(
485            &scalar_columnar_value.into_array_of_size(100).unwrap(),
486            &expected_array
487        );
488
489        // Array case with wrong size
490        let arr = make_array(1, 3);
491        let arr_columnar_value = ColumnarValue::Array(Arc::clone(&arr));
492        let result = arr_columnar_value.into_array_of_size(5);
493        let err = result.unwrap_err();
494        assert!(
495            err.to_string().starts_with(
496                "Internal error: Array length 3 does not match expected length 5"
497            ),
498            "Found: {err}"
499        );
500    }
501
502    #[test]
503    fn values_to_arrays() {
504        // (input, expected)
505        let cases = vec![
506            // empty
507            TestCase {
508                input: vec![],
509                expected: vec![],
510            },
511            // one array of length 3
512            TestCase {
513                input: vec![ColumnarValue::Array(make_array(1, 3))],
514                expected: vec![make_array(1, 3)],
515            },
516            // two arrays length 3
517            TestCase {
518                input: vec![
519                    ColumnarValue::Array(make_array(1, 3)),
520                    ColumnarValue::Array(make_array(2, 3)),
521                ],
522                expected: vec![make_array(1, 3), make_array(2, 3)],
523            },
524            // array and scalar
525            TestCase {
526                input: vec![
527                    ColumnarValue::Array(make_array(1, 3)),
528                    ColumnarValue::Scalar(ScalarValue::Int32(Some(100))),
529                ],
530                expected: vec![
531                    make_array(1, 3),
532                    make_array(100, 3), // scalar is expanded
533                ],
534            },
535            // scalar and array
536            TestCase {
537                input: vec![
538                    ColumnarValue::Scalar(ScalarValue::Int32(Some(100))),
539                    ColumnarValue::Array(make_array(1, 3)),
540                ],
541                expected: vec![
542                    make_array(100, 3), // scalar is expanded
543                    make_array(1, 3),
544                ],
545            },
546            // multiple scalars and array
547            TestCase {
548                input: vec![
549                    ColumnarValue::Scalar(ScalarValue::Int32(Some(100))),
550                    ColumnarValue::Array(make_array(1, 3)),
551                    ColumnarValue::Scalar(ScalarValue::Int32(Some(200))),
552                ],
553                expected: vec![
554                    make_array(100, 3), // scalar is expanded
555                    make_array(1, 3),
556                    make_array(200, 3), // scalar is expanded
557                ],
558            },
559        ];
560        for case in cases {
561            case.run();
562        }
563    }
564
565    #[test]
566    #[should_panic(
567        expected = "Arguments has mixed length. Expected length: 3, found length: 4"
568    )]
569    fn values_to_arrays_mixed_length() {
570        ColumnarValue::values_to_arrays(&[
571            ColumnarValue::Array(make_array(1, 3)),
572            ColumnarValue::Array(make_array(2, 4)),
573        ])
574        .unwrap();
575    }
576
577    #[test]
578    #[should_panic(
579        expected = "Arguments has mixed length. Expected length: 3, found length: 7"
580    )]
581    fn values_to_arrays_mixed_length_and_scalar() {
582        ColumnarValue::values_to_arrays(&[
583            ColumnarValue::Array(make_array(1, 3)),
584            ColumnarValue::Scalar(ScalarValue::Int32(Some(100))),
585            ColumnarValue::Array(make_array(2, 7)),
586        ])
587        .unwrap();
588    }
589
590    struct TestCase {
591        input: Vec<ColumnarValue>,
592        expected: Vec<ArrayRef>,
593    }
594
595    impl TestCase {
596        fn run(self) {
597            let Self { input, expected } = self;
598
599            assert_eq!(
600                ColumnarValue::values_to_arrays(&input).unwrap(),
601                expected,
602                "\ninput: {input:?}\nexpected: {expected:?}"
603            );
604        }
605    }
606
607    /// Makes an array of length `len` with all elements set to `val`
608    fn make_array(val: i32, len: usize) -> ArrayRef {
609        Arc::new(Int32Array::from(vec![val; len]))
610    }
611
612    #[test]
613    fn test_display_scalar() {
614        let column = ColumnarValue::from(ScalarValue::from("foo"));
615        assert_eq!(
616            column.to_string(),
617            concat!(
618                "+----------------------------+\n",
619                "| ColumnarValue(ScalarValue) |\n",
620                "+----------------------------+\n",
621                "| foo                        |\n",
622                "+----------------------------+"
623            )
624        );
625    }
626
627    #[test]
628    fn test_display_array() {
629        let array: ArrayRef = Arc::new(Int32Array::from_iter_values(vec![1, 2, 3]));
630        let column = ColumnarValue::from(array);
631        assert_eq!(
632            column.to_string(),
633            concat!(
634                "+-------------------------+\n",
635                "| ColumnarValue(ArrayRef) |\n",
636                "+-------------------------+\n",
637                "| 1                       |\n",
638                "| 2                       |\n",
639                "| 3                       |\n",
640                "+-------------------------+"
641            )
642        );
643    }
644
645    #[test]
646    fn cast_struct_by_field_name() {
647        let source_fields = Fields::from(vec![
648            Field::new("b", DataType::Int32, true),
649            Field::new("a", DataType::Int32, true),
650        ]);
651
652        let target_fields = Fields::from(vec![
653            Field::new("a", DataType::Int32, true),
654            Field::new("b", DataType::Int32, true),
655        ]);
656
657        let struct_array = StructArray::new(
658            source_fields,
659            vec![
660                Arc::new(Int32Array::from(vec![Some(3)])),
661                Arc::new(Int32Array::from(vec![Some(4)])),
662            ],
663            None,
664        );
665
666        let value = ColumnarValue::Array(Arc::new(struct_array));
667        let casted = value
668            .cast_to(&DataType::Struct(target_fields.clone()), None)
669            .expect("struct cast should succeed");
670
671        let ColumnarValue::Array(arr) = casted else {
672            panic!("expected array after cast");
673        };
674
675        let struct_array = arr
676            .as_any()
677            .downcast_ref::<StructArray>()
678            .expect("expected StructArray");
679
680        let field_a = struct_array
681            .column_by_name("a")
682            .expect("expected field a in cast result");
683        let field_b = struct_array
684            .column_by_name("b")
685            .expect("expected field b in cast result");
686
687        assert_eq!(
688            field_a
689                .as_any()
690                .downcast_ref::<Int32Array>()
691                .expect("expected Int32 array")
692                .value(0),
693            4
694        );
695        assert_eq!(
696            field_b
697                .as_any()
698                .downcast_ref::<Int32Array>()
699                .expect("expected Int32 array")
700                .value(0),
701            3
702        );
703    }
704
705    #[test]
706    fn cast_struct_missing_field_inserts_nulls() {
707        let source_fields = Fields::from(vec![Field::new("a", DataType::Int32, true)]);
708
709        let target_fields = Fields::from(vec![
710            Field::new("a", DataType::Int32, true),
711            Field::new("b", DataType::Int32, true),
712        ]);
713
714        let struct_array = StructArray::new(
715            source_fields,
716            vec![Arc::new(Int32Array::from(vec![Some(5)]))],
717            None,
718        );
719
720        let value = ColumnarValue::Array(Arc::new(struct_array));
721        let casted = value
722            .cast_to(&DataType::Struct(target_fields.clone()), None)
723            .expect("struct cast should succeed");
724
725        let ColumnarValue::Array(arr) = casted else {
726            panic!("expected array after cast");
727        };
728
729        let struct_array = arr
730            .as_any()
731            .downcast_ref::<StructArray>()
732            .expect("expected StructArray");
733
734        let field_b = struct_array
735            .column_by_name("b")
736            .expect("expected missing field to be added");
737
738        assert!(field_b.is_null(0));
739    }
740
741    #[test]
742    fn cast_date64_array_to_timestamp_overflow() {
743        let overflow_value = i64::MAX / 1_000_000 + 1;
744        let array: ArrayRef = Arc::new(Date64Array::from(vec![Some(overflow_value)]));
745        let value = ColumnarValue::Array(array);
746        let result =
747            value.cast_to(&DataType::Timestamp(TimeUnit::Nanosecond, None), None);
748        let err = result.expect_err("expected overflow to be detected");
749        assert!(
750            err.to_string()
751                .contains("converted value exceeds the representable i64 range"),
752            "unexpected error: {err}"
753        );
754    }
755
756    #[test]
757    fn cast_timestamp_array_to_timestamp_overflow() {
758        let overflow_value = i64::MAX / 1_000_000_000 + 1;
759        let array: ArrayRef =
760            Arc::new(TimestampSecondArray::from(vec![Some(overflow_value)]));
761        let value = ColumnarValue::Array(array);
762        let result =
763            value.cast_to(&DataType::Timestamp(TimeUnit::Nanosecond, None), None);
764        let err = result.expect_err("expected overflow to be detected");
765        assert!(
766            err.to_string()
767                .contains("converted value exceeds the representable i64 range"),
768            "unexpected error: {err}"
769        );
770    }
771
772    #[test]
773    fn safe_cast_timestamp_array_to_timestamp_overflow_returns_null() {
774        let overflow_value = i64::MAX / 1_000_000_000 + 1;
775        let array: ArrayRef =
776            Arc::new(TimestampSecondArray::from(vec![Some(overflow_value)]));
777        let value = ColumnarValue::Array(array);
778        let safe_options = CastOptions {
779            safe: true,
780            ..DEFAULT_CAST_OPTIONS
781        };
782
783        let casted = value
784            .cast_to(
785                &DataType::Timestamp(TimeUnit::Nanosecond, None),
786                Some(&safe_options),
787            )
788            .expect("expected safe cast to return null");
789
790        let ColumnarValue::Array(array) = casted else {
791            panic!("expected array after cast");
792        };
793        let array = array
794            .as_any()
795            .downcast_ref::<TimestampNanosecondArray>()
796            .expect("expected TimestampNanosecondArray");
797        assert!(array.is_null(0));
798    }
799}