## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/expr-common/src/type_coercion/binary.rs

    Line
    Count
    Source
    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
    //! Coercion rules for matching argument types for binary operators
    19
    20
    use std::collections::HashSet;
    21
    use std::sync::Arc;
    22
    23
    use crate::operator::Operator;
    24
    25
    use arrow::array::{new_empty_array, Array};
    26
    use arrow::compute::can_cast_types;
    27
    use arrow::datatypes::{
    28
        DataType, Field, FieldRef, Fields, TimeUnit, DECIMAL128_MAX_PRECISION,
    29
        DECIMAL128_MAX_SCALE, DECIMAL256_MAX_PRECISION, DECIMAL256_MAX_SCALE,
    30
        DECIMAL32_MAX_PRECISION, DECIMAL32_MAX_SCALE, DECIMAL64_MAX_PRECISION,
    31
        DECIMAL64_MAX_SCALE,
    32
    };
    33
    use datafusion_common::types::NativeType;
    34
    use datafusion_common::{
    35
        exec_err, internal_err, not_impl_err, plan_datafusion_err, plan_err, Diagnostic,
    36
        Result, Span, Spans,
    37
    };
    38
    use itertools::Itertools;
    39
    40
    /// The type signature of an instantiation of binary operator expression such as
    41
    /// `lhs + rhs`
    42
    ///
    43
    /// Note this is different than [`crate::signature::Signature`] which
    44
    /// describes the type signature of a function.
    45
    struct Signature {
    46
        /// The type to coerce the left argument to
    47
        lhs: DataType,
    48
        /// The type to coerce the right argument to
    49
        rhs: DataType,
    50
        /// The return type of the expression
    51
        ret: DataType,
    52
    }
    53
    54
    impl Signature {
    55
        /// A signature where the inputs are the same type as the output
    56
    0
        fn uniform(t: DataType) -> Self {
    57
    0
            Self {
    58
    0
                lhs: t.clone(),
    59
    0
                rhs: t.clone(),
    60
    0
                ret: t,
    61
    0
            }
    62
    0
        }
    63
    64
        /// A signature where the inputs are the same type with a boolean output
    65
    0
        fn comparison(t: DataType) -> Self {
    66
    0
            Self {
    67
    0
                lhs: t.clone(),
    68
    0
                rhs: t,
    69
    0
                ret: DataType::Boolean,
    70
    0
            }
    71
    0
        }
    72
    }
    73
    74
    /// Provides type information about a binary expression, coercing different
    75
    /// input types into a sensible output type.
    76
    pub struct BinaryTypeCoercer<'a> {
    77
        lhs: &'a DataType,
    78
        op: &'a Operator,
    79
        rhs: &'a DataType,
    80
    81
        lhs_spans: Spans,
    82
        op_spans: Spans,
    83
        rhs_spans: Spans,
    84
    }
    85
    86
    impl<'a> BinaryTypeCoercer<'a> {
    87
        /// Creates a new [`BinaryTypeCoercer`], for reasoning about the input
    88
        /// and output types of a binary expression.
    89
    0
        pub fn new(lhs: &'a DataType, op: &'a Operator, rhs: &'a DataType) -> Self {
    90
    0
            Self {
    91
    0
                lhs,
    92
    0
                op,
    93
    0
                rhs,
    94
    0
                lhs_spans: Spans::new(),
    95
    0
                op_spans: Spans::new(),
    96
    0
                rhs_spans: Spans::new(),
    97
    0
            }
    98
    0
        }
    99
    100
        /// Sets the spans information for the left side of the binary expression,
    101
        /// so better diagnostics can be provided in case of errors.
    102
    0
        pub fn set_lhs_spans(&mut self, spans: Spans) {
    103
    0
            self.lhs_spans = spans;
    104
    0
        }
    105
    106
        /// Sets the spans information for the operator of the binary expression, so
    107
        /// better diagnostics can be provided in case of errors.
    108
    0
        pub fn set_op_spans(&mut self, spans: Spans) {
    109
    0
            self.op_spans = spans;
    110
    0
        }
    111
    112
        /// Sets the spans information for the right side of the binary expression,
    113
        /// so better diagnostics can be provided in case of errors.
    114
    0
        pub fn set_rhs_spans(&mut self, spans: Spans) {
    115
    0
            self.rhs_spans = spans;
    116
    0
        }
    117
    118
    0
        fn span(&self) -> Option<Span> {
    119
    0
            Span::union_iter(
    120
    0
                [self.lhs_spans.first(), self.rhs_spans.first()]
    121
    0
                    .iter()
    122
    0
                    .copied()
    123
    0
                    .flatten(),
    124
            )
    125
    0
        }
    126
    127
        /// Returns a [`Signature`] for applying `op` to arguments of type `lhs` and `rhs`
    128
    0
        fn signature(&'a self) -> Result<Signature> {
    129
            // Special handling for arithmetic operations with both `lhs` and `rhs` NULL:
    130
            // When both operands are NULL, we are providing a concrete numeric type (Int64)
    131
            // to allow the arithmetic operation to proceed. This ensures NULL `op` NULL returns NULL
    132
            // instead of failing during planning.
    133
    0
            if matches!((self.lhs, self.rhs), (DataType::Null, DataType::Null))
    134
    0
                && self.op.is_numerical_operators()
    135
            {
    136
    0
                return Ok(Signature::uniform(DataType::Int64));
    137
    0
            }
    138
    139
    0
            if let Some(coerced) = null_coercion(self.lhs, self.rhs) {
    140
                // Special handling for arithmetic + null coercion:
    141
                // For arithmetic operators on non-temporal types, we must handle the result type here using Arrow's numeric kernel.
    142
                // This is because Arrow expects concrete numeric types, and this ensures the correct result type (e.g., for NULL + Int32, result is Int32).
    143
                // For all other cases (including temporal arithmetic and non-arithmetic operators),
    144
                // we can delegate to signature_inner(&coerced, &coerced), which handles the necessary logic for those operators.
    145
                // In those cases, signature_inner is designed to work with the coerced type, even if it originated from a NULL.
    146
    0
                if self.op.is_numerical_operators() && !coerced.is_temporal() {
    147
    0
                    let ret = self.get_result(&coerced, &coerced).map_err(|e| {
    148
    0
                        plan_datafusion_err!(
    149
    0
                            "Cannot get result type for arithmetic operation {coerced} {} {coerced}: {e}",
    150
                            self.op
    151
                        )
    152
    0
                    })?;
    153
    154
    0
                    return Ok(Signature {
    155
    0
                        lhs: coerced.clone(),
    156
    0
                        rhs: coerced,
    157
    0
                        ret,
    158
    0
                    });
    159
    0
                }
    160
    0
                return self.signature_inner(&coerced, &coerced);
    161
    0
            }
    162
    0
            self.signature_inner(self.lhs, self.rhs)
    163
    0
        }
    164
    165
        /// Returns the result type for arithmetic operations
    166
    0
        fn get_result(
    167
    0
            &self,
    168
    0
            lhs: &DataType,
    169
    0
            rhs: &DataType,
    170
    0
        ) -> arrow::error::Result<DataType> {
    171
            use arrow::compute::kernels::numeric::*;
    172
    0
            let l = new_empty_array(lhs);
    173
    0
            let r = new_empty_array(rhs);
    174
    175
    0
            let result = match self.op {
    176
    0
                Operator::Plus => add_wrapping(&l, &r),
    177
    0
                Operator::Minus => sub_wrapping(&l, &r),
    178
    0
                Operator::Multiply => mul_wrapping(&l, &r),
    179
    0
                Operator::Divide => div(&l, &r),
    180
    0
                Operator::Modulo => rem(&l, &r),
    181
    0
                _ => unreachable!(),
    182
            };
    183
    0
            result.map(|x| x.data_type().clone())
    184
    0
        }
    185
    186
    0
        fn signature_inner(&'a self, lhs: &DataType, rhs: &DataType) -> Result<Signature> {
    187
            use arrow::datatypes::DataType::*;
    188
            use Operator::*;
    189
    0
            let result = match self.op {
    190
            Eq |
    191
            NotEq |
    192
            Lt |
    193
            LtEq |
    194
            Gt |
    195
            GtEq |
    196
            IsDistinctFrom |
    197
            IsNotDistinctFrom => {
    198
    0
                comparison_coercion(lhs, rhs).map(Signature::comparison).ok_or_else(|| {
    199
    0
                    plan_datafusion_err!(
    200
    0
                        "Cannot infer common argument type for comparison operation {} {} {}",
    201
                        self.lhs,
    202
                        self.op,
    203
                        self.rhs
    204
                    )
    205
    0
                })
    206
            }
    207
    0
            And | Or => if matches!((lhs, rhs), (Boolean | Null, Boolean | Null)) {
    208
                // Logical binary boolean operators can only be evaluated for
    209
                // boolean or null arguments.
    210
    0
                Ok(Signature::uniform(Boolean))
    211
            } else {
    212
    0
                plan_err!(
    213
    0
                    "Cannot infer common argument type for logical boolean operation {} {} {}", self.lhs, self.op, self.rhs
    214
                )
    215
            }
    216
            RegexMatch | RegexIMatch | RegexNotMatch | RegexNotIMatch => {
    217
    0
                regex_coercion(lhs, rhs).map(Signature::comparison).ok_or_else(|| {
    218
    0
                    plan_datafusion_err!(
    219
    0
                        "Cannot infer common argument type for regex operation {} {} {}", self.lhs, self.op, self.rhs
    220
                    )
    221
    0
                })
    222
            }
    223
            LikeMatch | ILikeMatch | NotLikeMatch | NotILikeMatch => {
    224
    0
                regex_coercion(lhs, rhs).map(Signature::comparison).ok_or_else(|| {
    225
    0
                    plan_datafusion_err!(
    226
    0
                        "Cannot infer common argument type for regex operation {} {} {}", self.lhs, self.op, self.rhs
    227
                    )
    228
    0
                })
    229
            }
    230
            BitwiseAnd | BitwiseOr | BitwiseXor | BitwiseShiftRight | BitwiseShiftLeft => {
    231
    0
                bitwise_coercion(lhs, rhs).map(Signature::uniform).ok_or_else(|| {
    232
    0
                    plan_datafusion_err!(
    233
    0
                        "Cannot infer common type for bitwise operation {} {} {}", self.lhs, self.op, self.rhs
    234
                    )
    235
    0
                })
    236
            }
    237
            StringConcat => {
    238
    0
                string_concat_coercion(lhs, rhs).map(Signature::uniform).ok_or_else(|| {
    239
    0
                    plan_datafusion_err!(
    240
    0
                        "Cannot infer common string type for string concat operation {} {} {}", self.lhs, self.op, self.rhs
    241
                    )
    242
    0
                })
    243
            }
    244
            AtArrow | ArrowAt => {
    245
                // Array contains or search (similar to LIKE) operation
    246
    0
                array_coercion(lhs, rhs)
    247
    0
                    .or_else(|| like_coercion(lhs, rhs)).map(Signature::comparison).ok_or_else(|| {
    248
    0
                        plan_datafusion_err!(
    249
    0
                            "Cannot infer common argument type for operation {} {} {}", self.lhs, self.op, self.rhs
    250
                        )
    251
    0
                    })
    252
            }
    253
            AtAt => {
    254
                // text search has similar signature to LIKE
    255
    0
                like_coercion(lhs, rhs).map(Signature::comparison).ok_or_else(|| {
    256
    0
                    plan_datafusion_err!(
    257
    0
                        "Cannot infer common argument type for AtAt operation {} {} {}", self.lhs, self.op, self.rhs
    258
                    )
    259
    0
                })
    260
            }
    261
            Plus | Minus | Multiply | Divide | Modulo  =>  {
    262
    0
                if let Ok(ret) = self.get_result(lhs, rhs) {
    263
                    // Temporal arithmetic, e.g. Date32 + Interval
    264
    0
                    Ok(Signature{
    265
    0
                        lhs: lhs.clone(),
    266
    0
                        rhs: rhs.clone(),
    267
    0
                        ret,
    268
    0
                    })
    269
    0
                } else if let Some(coerced) = temporal_coercion_strict_timezone(lhs, rhs) {
    270
                    // Temporal arithmetic by first coercing to a common time representation
    271
                    // e.g. Date32 - Timestamp
    272
    0
                    let ret = self.get_result(&coerced, &coerced).map_err(|e| {
    273
    0
                        plan_datafusion_err!(
    274
    0
                            "Cannot get result type for temporal operation {coerced} {} {coerced}: {e}", self.op
    275
                        )
    276
    0
                    })?;
    277
    0
                    Ok(Signature{
    278
    0
                        lhs: coerced.clone(),
    279
    0
                        rhs: coerced,
    280
    0
                        ret,
    281
    0
                    })
    282
    0
                } else if let Some((lhs, rhs)) = math_decimal_coercion(lhs, rhs) {
    283
                    // Decimal arithmetic, e.g. Decimal(10, 2) + Decimal(10, 0)
    284
    0
                    let ret = self.get_result(&lhs, &rhs).map_err(|e| {
    285
    0
                        plan_datafusion_err!(
    286
    0
                            "Cannot get result type for decimal operation {} {} {}: {e}", self.lhs, self.op, self.rhs
    287
                        )
    288
    0
                    })?;
    289
    0
                    Ok(Signature{
    290
    0
                        lhs,
    291
    0
                        rhs,
    292
    0
                        ret,
    293
    0
                    })
    294
    0
                } else if let Some(numeric) = mathematics_numerical_coercion(lhs, rhs) {
    295
                    // Numeric arithmetic, e.g. Int32 + Int32
    296
    0
                    Ok(Signature::uniform(numeric))
    297
                } else {
    298
    0
                    plan_err!(
    299
    0
                        "Cannot coerce arithmetic expression {} {} {} to valid types", self.lhs, self.op, self.rhs
    300
                    )
    301
                }
    302
            },
    303
            IntegerDivide | Arrow | LongArrow | HashArrow | HashLongArrow
    304
            | HashMinus | AtQuestion | Question | QuestionAnd | QuestionPipe => {
    305
    0
                not_impl_err!("Operator {} is not yet supported", self.op)
    306
            }
    307
        };
    308
    0
            result.map_err(|err| {
    309
    0
                let diagnostic =
    310
    0
                    Diagnostic::new_error("expressions have incompatible types", self.span())
    311
    0
                        .with_note(format!("has type {}", self.lhs), self.lhs_spans.first())
    312
    0
                        .with_note(format!("has type {}", self.rhs), self.rhs_spans.first());
    313
    0
                err.with_diagnostic(diagnostic)
    314
    0
            })
    315
    0
        }
    316
    317
        /// Returns the resulting type of a binary expression evaluating the `op` with the left and right hand types
    318
    0
        pub fn get_result_type(&'a self) -> Result<DataType> {
    319
    0
            self.signature().map(|sig| sig.ret)
    320
    0
        }
    321
    322
        /// Returns the coerced input types for a binary expression evaluating the `op` with the left and right hand types
    323
    0
        pub fn get_input_types(&'a self) -> Result<(DataType, DataType)> {
    324
    0
            self.signature().map(|sig| (sig.lhs, sig.rhs))
    325
    0
        }
    326
    }
    327
    328
    // TODO Move the rest inside of BinaryTypeCoercer
    329
    330
    1.64k
    fn is_decimal(data_type: &DataType) -> bool {
    331
    824
        matches!(
    332
    1.64k
            data_type,
    333
            DataType::Decimal32(..)
    334
                | DataType::Decimal64(..)
    335
                | DataType::Decimal128(..)
    336
                | DataType::Decimal256(..)
    337
        )
    338
    1.64k
    }
    339
    340
    /// Coercion rules for mathematics operators between decimal and non-decimal types.
    341
    0
    fn math_decimal_coercion(
    342
    0
        lhs_type: &DataType,
    343
    0
        rhs_type: &DataType,
    344
    0
    ) -> Option<(DataType, DataType)> {
    345
        use arrow::datatypes::DataType::*;
    346
    347
    0
        match (lhs_type, rhs_type) {
    348
    0
            (Dictionary(_, value_type), _) => {
    349
    0
                let (value_type, rhs_type) = math_decimal_coercion(value_type, rhs_type)?;
    350
    0
                Some((value_type, rhs_type))
    351
            }
    352
    0
            (_, Dictionary(_, value_type)) => {
    353
    0
                let (lhs_type, value_type) = math_decimal_coercion(lhs_type, value_type)?;
    354
    0
                Some((lhs_type, value_type))
    355
            }
    356
            (
    357
                Null,
    358
                Decimal32(_, _) | Decimal64(_, _) | Decimal128(_, _) | Decimal256(_, _),
    359
    0
            ) => Some((rhs_type.clone(), rhs_type.clone())),
    360
            (
    361
                Decimal32(_, _) | Decimal64(_, _) | Decimal128(_, _) | Decimal256(_, _),
    362
                Null,
    363
    0
            ) => Some((lhs_type.clone(), lhs_type.clone())),
    364
            (Decimal32(_, _), Decimal32(_, _))
    365
            | (Decimal64(_, _), Decimal64(_, _))
    366
            | (Decimal128(_, _), Decimal128(_, _))
    367
            | (Decimal256(_, _), Decimal256(_, _)) => {
    368
    0
                Some((lhs_type.clone(), rhs_type.clone()))
    369
            }
    370
            // Cross-variant decimal coercion - choose larger variant with appropriate precision/scale
    371
    0
            (lhs, rhs)
    372
    0
                if is_decimal(lhs)
    373
    0
                    && is_decimal(rhs)
    374
    0
                    && std::mem::discriminant(lhs) != std::mem::discriminant(rhs) =>
    375
            {
    376
    0
                let coerced_type = get_wider_decimal_type_cross_variant(lhs_type, rhs_type)?;
    377
    0
                Some((coerced_type.clone(), coerced_type))
    378
            }
    379
            // Unlike with comparison we don't coerce to a decimal in the case of floating point
    380
            // numbers, instead falling back to floating point arithmetic instead
    381
            (
    382
                Decimal32(_, _),
    383
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    384
            ) => Some((
    385
    0
                lhs_type.clone(),
    386
    0
                coerce_numeric_type_to_decimal32(rhs_type)?,
    387
            )),
    388
            (
    389
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    390
                Decimal32(_, _),
    391
            ) => Some((
    392
    0
                coerce_numeric_type_to_decimal32(lhs_type)?,
    393
    0
                rhs_type.clone(),
    394
            )),
    395
            (
    396
                Decimal64(_, _),
    397
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    398
            ) => Some((
    399
    0
                lhs_type.clone(),
    400
    0
                coerce_numeric_type_to_decimal64(rhs_type)?,
    401
            )),
    402
            (
    403
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    404
                Decimal64(_, _),
    405
            ) => Some((
    406
    0
                coerce_numeric_type_to_decimal64(lhs_type)?,
    407
    0
                rhs_type.clone(),
    408
            )),
    409
            (
    410
                Decimal128(_, _),
    411
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    412
            ) => Some((
    413
    0
                lhs_type.clone(),
    414
    0
                coerce_numeric_type_to_decimal128(rhs_type)?,
    415
            )),
    416
            (
    417
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    418
                Decimal128(_, _),
    419
            ) => Some((
    420
    0
                coerce_numeric_type_to_decimal128(lhs_type)?,
    421
    0
                rhs_type.clone(),
    422
            )),
    423
            (
    424
                Decimal256(_, _),
    425
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    426
            ) => Some((
    427
    0
                lhs_type.clone(),
    428
    0
                coerce_numeric_type_to_decimal256(rhs_type)?,
    429
            )),
    430
            (
    431
                Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64,
    432
                Decimal256(_, _),
    433
            ) => Some((
    434
    0
                coerce_numeric_type_to_decimal256(lhs_type)?,
    435
    0
                rhs_type.clone(),
    436
            )),
    437
    0
            _ => None,
    438
        }
    439
    0
    }
    440
    441
    /// Returns the output type of applying bitwise operations such as
    442
    /// `&`, `|`, or `xor`to arguments of `lhs_type` and `rhs_type`.
    443
    0
    fn bitwise_coercion(left_type: &DataType, right_type: &DataType) -> Option<DataType> {
    444
        use arrow::datatypes::DataType::*;
    445
    446
    0
        if !both_numeric_or_null_and_numeric(left_type, right_type) {
    447
    0
            return None;
    448
    0
        }
    449
    450
    0
        if left_type == right_type {
    451
    0
            return Some(left_type.clone());
    452
    0
        }
    453
    454
    0
        match (left_type, right_type) {
    455
    0
            (UInt64, _) | (_, UInt64) => Some(UInt64),
    456
            (Int64, _)
    457
            | (_, Int64)
    458
            | (UInt32, Int8)
    459
            | (Int8, UInt32)
    460
            | (UInt32, Int16)
    461
            | (Int16, UInt32)
    462
            | (UInt32, Int32)
    463
    0
            | (Int32, UInt32) => Some(Int64),
    464
            (Int32, _)
    465
            | (_, Int32)
    466
            | (UInt16, Int16)
    467
            | (Int16, UInt16)
    468
            | (UInt16, Int8)
    469
    0
            | (Int8, UInt16) => Some(Int32),
    470
    0
            (UInt32, _) | (_, UInt32) => Some(UInt32),
    471
    0
            (Int16, _) | (_, Int16) | (Int8, UInt8) | (UInt8, Int8) => Some(Int16),
    472
    0
            (UInt16, _) | (_, UInt16) => Some(UInt16),
    473
    0
            (Int8, _) | (_, Int8) => Some(Int8),
    474
    0
            (UInt8, _) | (_, UInt8) => Some(UInt8),
    475
    0
            _ => None,
    476
        }
    477
    0
    }
    478
    479
    #[derive(Debug, PartialEq, Eq, Hash, Clone)]
    480
    enum TypeCategory {
    481
        Array,
    482
        Boolean,
    483
        Numeric,
    484
        // String, well-defined type, but are considered as unknown type.
    485
        DateTime,
    486
        Composite,
    487
        Unknown,
    488
        NotSupported,
    489
    }
    490
    491
    impl From<&DataType> for TypeCategory {
    492
    0
        fn from(data_type: &DataType) -> Self {
    493
    0
            match data_type {
    494
                // Dict is a special type in arrow, we check the value type
    495
    0
                DataType::Dictionary(_, v) => {
    496
    0
                    let v = v.as_ref();
    497
    0
                    TypeCategory::from(v)
    498
                }
    499
                _ => {
    500
    0
                    if data_type.is_numeric() {
    501
    0
                        return TypeCategory::Numeric;
    502
    0
                    }
    503
    504
    0
                    if matches!(data_type, DataType::Boolean) {
    505
    0
                        return TypeCategory::Boolean;
    506
    0
                    }
    507
    508
    0
                    if matches!(
    509
    0
                        data_type,
    510
                        DataType::List(_)
    511
                            | DataType::FixedSizeList(_, _)
    512
                            | DataType::LargeList(_)
    513
                    ) {
    514
    0
                        return TypeCategory::Array;
    515
    0
                    }
    516
    517
                    // It is categorized as unknown type because the type will be resolved later on
    518
    0
                    if matches!(
    519
    0
                        data_type,
    520
                        DataType::Utf8
    521
                            | DataType::LargeUtf8
    522
                            | DataType::Utf8View
    523
                            | DataType::Null
    524
                    ) {
    525
    0
                        return TypeCategory::Unknown;
    526
    0
                    }
    527
    528
    0
                    if matches!(
    529
    0
                        data_type,
    530
                        DataType::Date32
    531
                            | DataType::Date64
    532
                            | DataType::Time32(_)
    533
                            | DataType::Time64(_)
    534
                            | DataType::Timestamp(_, _)
    535
                            | DataType::Interval(_)
    536
                            | DataType::Duration(_)
    537
                    ) {
    538
    0
                        return TypeCategory::DateTime;
    539
    0
                    }
    540
    541
    0
                    if matches!(
    542
    0
                        data_type,
    543
                        DataType::Map(_, _) | DataType::Struct(_) | DataType::Union(_, _)
    544
                    ) {
    545
    0
                        return TypeCategory::Composite;
    546
    0
                    }
    547
    548
    0
                    TypeCategory::NotSupported
    549
                }
    550
            }
    551
    0
        }
    552
    }
    553
    554
    /// Coerce dissimilar data types to a single data type.
    555
    /// UNION, INTERSECT, EXCEPT, CASE, ARRAY, VALUES, and the GREATEST and LEAST functions are
    556
    /// examples that has the similar resolution rules.
    557
    /// See <https://www.postgresql.org/docs/current/typeconv-union-case.html> for more information.
    558
    /// The rules in the document provide a clue, but adhering strictly to them doesn't precisely
    559
    /// align with the behavior of Postgres. Therefore, we've made slight adjustments to the rules
    560
    /// to better match the behavior of both Postgres and DuckDB. For example, we expect adjusted
    561
    /// decimal precision and scale when coercing decimal types.
    562
    ///
    563
    /// This function doesn't preserve correct field name and nullability for the struct type, we only care about data type.
    564
    ///
    565
    /// Returns Option because we might want to continue on the code even if the data types are not coercible to the common type
    566
    0
    pub fn type_union_resolution(data_types: &[DataType]) -> Option<DataType> {
    567
    0
        if data_types.is_empty() {
    568
    0
            return None;
    569
    0
        }
    570
    571
        // If all the data_types is the same return first one
    572
    0
        if data_types.iter().all(|t| t == &data_types[0]) {
    573
    0
            return Some(data_types[0].clone());
    574
    0
        }
    575
    576
        // If all the data_types are null, return string
    577
    0
        if data_types.iter().all(|t| t == &DataType::Null) {
    578
    0
            return Some(DataType::Utf8View);
    579
    0
        }
    580
    581
        // Ignore Nulls, if any data_type category is not the same, return None
    582
    0
        let data_types_category: Vec<TypeCategory> = data_types
    583
    0
            .iter()
    584
    0
            .filter(|&t| t != &DataType::Null)
    585
    0
            .map(|t| t.into())
    586
    0
            .collect();
    587
    588
    0
        if data_types_category
    589
    0
            .iter()
    590
    0
            .any(|t| t == &TypeCategory::NotSupported)
    591
        {
    592
    0
            return None;
    593
    0
        }
    594
    595
        // Check if there is only one category excluding Unknown
    596
    0
        let categories: HashSet<TypeCategory> = HashSet::from_iter(
    597
    0
            data_types_category
    598
    0
                .iter()
    599
    0
                .filter(|&c| c != &TypeCategory::Unknown)
    600
    0
                .cloned(),
    601
        );
    602
    0
        if categories.len() > 1 {
    603
    0
            return None;
    604
    0
        }
    605
    606
        // Ignore Nulls
    607
    0
        let mut candidate_type: Option<DataType> = None;
    608
    0
        for data_type in data_types.iter() {
    609
    0
            if data_type == &DataType::Null {
    610
    0
                continue;
    611
    0
            }
    612
    0
            if let Some(ref candidate_t) = candidate_type {
    613
                // Find candidate type that all the data types can be coerced to
    614
                // Follows the behavior of Postgres and DuckDB
    615
                // Coerced type may be different from the candidate and current data type
    616
                // For example,
    617
                //  i64 and decimal(7, 2) are expect to get coerced type decimal(22, 2)
    618
                //  numeric string ('1') and numeric (2) are expect to get coerced type numeric (1, 2)
    619
    0
                if let Some(t) = type_union_resolution_coercion(data_type, candidate_t) {
    620
    0
                    candidate_type = Some(t);
    621
    0
                } else {
    622
    0
                    return None;
    623
                }
    624
    0
            } else {
    625
    0
                candidate_type = Some(data_type.clone());
    626
    0
            }
    627
        }
    628
    629
    0
        candidate_type
    630
    0
    }
    631
    632
    /// Coerce `lhs_type` and `rhs_type` to a common type for [type_union_resolution]
    633
    /// See [type_union_resolution] for more information.
    634
    0
    fn type_union_resolution_coercion(
    635
    0
        lhs_type: &DataType,
    636
    0
        rhs_type: &DataType,
    637
    0
    ) -> Option<DataType> {
    638
    0
        if lhs_type == rhs_type {
    639
    0
            return Some(lhs_type.clone());
    640
    0
        }
    641
    642
    0
        match (lhs_type, rhs_type) {
    643
            (
    644
    0
                DataType::Dictionary(lhs_index_type, lhs_value_type),
    645
    0
                DataType::Dictionary(rhs_index_type, rhs_value_type),
    646
            ) => {
    647
    0
                let new_index_type =
    648
    0
                    type_union_resolution_coercion(lhs_index_type, rhs_index_type);
    649
    0
                let new_value_type =
    650
    0
                    type_union_resolution_coercion(lhs_value_type, rhs_value_type);
    651
    0
                if let (Some(new_index_type), Some(new_value_type)) =
    652
    0
                    (new_index_type, new_value_type)
    653
                {
    654
    0
                    Some(DataType::Dictionary(
    655
    0
                        Box::new(new_index_type),
    656
    0
                        Box::new(new_value_type),
    657
    0
                    ))
    658
                } else {
    659
    0
                    None
    660
                }
    661
            }
    662
    0
            (DataType::Dictionary(index_type, value_type), other_type)
    663
    0
            | (other_type, DataType::Dictionary(index_type, value_type)) => {
    664
    0
                match type_union_resolution_coercion(value_type, other_type) {
    665
                    // Dict with View type is redundant, use value type instead
    666
                    // TODO: Add binary view, list view with tests
    667
    0
                    Some(DataType::Utf8View) => Some(DataType::Utf8View),
    668
    0
                    Some(new_value_type) => Some(DataType::Dictionary(
    669
    0
                        index_type.clone(),
    670
    0
                        Box::new(new_value_type),
    671
    0
                    )),
    672
    0
                    None => None,
    673
                }
    674
            }
    675
    0
            (DataType::Struct(lhs), DataType::Struct(rhs)) => {
    676
    0
                if lhs.len() != rhs.len() {
    677
    0
                    return None;
    678
    0
                }
    679
    680
                // Search the field in the right hand side with the SAME field name
    681
    0
                fn search_corresponding_coerced_type(
    682
    0
                    lhs_field: &FieldRef,
    683
    0
                    rhs: &Fields,
    684
    0
                ) -> Option<DataType> {
    685
    0
                    for rhs_field in rhs.iter() {
    686
    0
                        if lhs_field.name() == rhs_field.name() {
    687
    0
                            if let Some(t) = type_union_resolution_coercion(
    688
    0
                                lhs_field.data_type(),
    689
    0
                                rhs_field.data_type(),
    690
    0
                            ) {
    691
    0
                                return Some(t);
    692
                            } else {
    693
    0
                                return None;
    694
                            }
    695
    0
                        }
    696
                    }
    697
    698
    0
                    None
    699
    0
                }
    700
    701
    0
                let coerced_types = lhs
    702
    0
                    .iter()
    703
    0
                    .map(|lhs_field| search_corresponding_coerced_type(lhs_field, rhs))
    704
    0
                    .collect::<Option<Vec<_>>>()?;
    705
    706
                // preserve the field name and nullability
    707
    0
                let orig_fields = std::iter::zip(lhs.iter(), rhs.iter());
    708
    709
    0
                let fields: Vec<FieldRef> = coerced_types
    710
    0
                    .into_iter()
    711
    0
                    .zip(orig_fields)
    712
    0
                    .map(|(datatype, (lhs, rhs))| coerce_fields(datatype, lhs, rhs))
    713
    0
                    .collect();
    714
    0
                Some(DataType::Struct(fields.into()))
    715
            }
    716
            _ => {
    717
                // Numeric coercion is the same as comparison coercion, both find the narrowest type
    718
                // that can accommodate both types
    719
    0
                binary_numeric_coercion(lhs_type, rhs_type)
    720
    0
                    .or_else(|| list_coercion(lhs_type, rhs_type))
    721
    0
                    .or_else(|| temporal_coercion_nonstrict_timezone(lhs_type, rhs_type))
    722
    0
                    .or_else(|| string_coercion(lhs_type, rhs_type))
    723
    0
                    .or_else(|| numeric_string_coercion(lhs_type, rhs_type))
    724
    0
                    .or_else(|| binary_coercion(lhs_type, rhs_type))
    725
            }
    726
        }
    727
    0
    }
    728
    729
    /// Handle type union resolution including struct type and others.
    730
    0
    pub fn try_type_union_resolution(data_types: &[DataType]) -> Result<Vec<DataType>> {
    731
    0
        let err = match try_type_union_resolution_with_struct(data_types) {
    732
    0
            Ok(struct_types) => return Ok(struct_types),
    733
    0
            Err(e) => Some(e),
    734
        };
    735
    736
    0
        if let Some(new_type) = type_union_resolution(data_types) {
    737
    0
            Ok(vec![new_type; data_types.len()])
    738
        } else {
    739
    0
            exec_err!("Fail to find the coerced type, errors: {:?}", err)
    740
        }
    741
    0
    }
    742
    743
    // Handle struct where we only change the data type but preserve the field name and nullability.
    744
    // Since field name is the key of the struct, so it shouldn't be updated to the common column name like "c0" or "c1"
    745
    0
    pub fn try_type_union_resolution_with_struct(
    746
    0
        data_types: &[DataType],
    747
    0
    ) -> Result<Vec<DataType>> {
    748
    0
        let mut keys_string: Option<String> = None;
    749
    0
        for data_type in data_types {
    750
    0
            if let DataType::Struct(fields) = data_type {
    751
    0
                let keys = fields.iter().map(|f| f.name().to_owned()).join(",");
    752
    0
                if let Some(ref k) = keys_string {
    753
    0
                    if *k != keys {
    754
    0
                        return exec_err!("Expect same keys for struct type but got mismatched pair {} and {}", *k, keys);
    755
    0
                    }
    756
    0
                } else {
    757
    0
                    keys_string = Some(keys);
    758
    0
                }
    759
            } else {
    760
    0
                return exec_err!("Expect to get struct but got {data_type}");
    761
            }
    762
        }
    763
    764
    0
        let mut struct_types: Vec<DataType> = if let DataType::Struct(fields) = &data_types[0]
    765
        {
    766
    0
            fields.iter().map(|f| f.data_type().to_owned()).collect()
    767
        } else {
    768
    0
            return internal_err!("Struct type is checked is the previous function, so this should be unreachable");
    769
        };
    770
    771
    0
        for data_type in data_types.iter().skip(1) {
    772
    0
            if let DataType::Struct(fields) = data_type {
    773
    0
                let incoming_struct_types: Vec<DataType> =
    774
    0
                    fields.iter().map(|f| f.data_type().to_owned()).collect();
    775
                // The order of field is verified above
    776
    0
                for (lhs_type, rhs_type) in
    777
    0
                    struct_types.iter_mut().zip(incoming_struct_types.iter())
    778
                {
    779
    0
                    if let Some(coerced_type) =
    780
    0
                        type_union_resolution_coercion(lhs_type, rhs_type)
    781
    0
                    {
    782
    0
                        *lhs_type = coerced_type;
    783
    0
                    } else {
    784
    0
                        return exec_err!(
    785
    0
                            "Fail to find the coerced type for {} and {}",
    786
                            lhs_type,
    787
                            rhs_type
    788
                        );
    789
                    }
    790
                }
    791
            } else {
    792
    0
                return exec_err!("Expect to get struct but got {data_type}");
    793
            }
    794
        }
    795
    796
    0
        let mut final_struct_types = vec![];
    797
    0
        for s in data_types {
    798
    0
            let mut new_fields = vec![];
    799
    0
            if let DataType::Struct(fields) = s {
    800
    0
                for (i, f) in fields.iter().enumerate() {
    801
    0
                    let field = Arc::unwrap_or_clone(Arc::clone(f))
    802
    0
                        .with_data_type(struct_types[i].to_owned());
    803
    0
                    new_fields.push(Arc::new(field));
    804
    0
                }
    805
    0
            }
    806
    0
            final_struct_types.push(DataType::Struct(new_fields.into()))
    807
        }
    808
    809
    0
        Ok(final_struct_types)
    810
    0
    }
    811
    812
    /// Coerce `lhs_type` and `rhs_type` to a common type for the purposes of a
    813
    /// comparison operation
    814
    ///
    815
    /// Example comparison operations are `lhs = rhs` and `lhs > rhs`
    816
    ///
    817
    /// Binary comparison kernels require the two arguments to be the (exact) same
    818
    /// data type. However, users can write queries where the two arguments are
    819
    /// different data types. In such cases, the data types are automatically cast
    820
    /// (coerced) to a single data type to pass to the kernels.
    821
    ///
    822
    /// # Numeric comparisons
    823
    ///
    824
    /// When comparing numeric values, the lower precision type is coerced to the
    825
    /// higher precision type to avoid losing data. For example when comparing
    826
    /// `Int32` to `Int64` the coerced type is `Int64` so the `Int32` argument will
    827
    /// be cast.
    828
    ///
    829
    /// # Numeric / String comparisons
    830
    ///
    831
    /// When comparing numeric values and strings, both values will be coerced to
    832
    /// strings.  For example when comparing `'2' > 1`,  the arguments will be
    833
    /// coerced to `Utf8` for comparison
    834
    648
    pub fn comparison_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    835
    648
        if lhs_type.equals_datatype(rhs_type) {
    836
            // same type => equality is possible
    837
    174
            return Some(lhs_type.clone());
    838
    474
        }
    839
    474
        binary_numeric_coercion(lhs_type, rhs_type)
    840
    474
            .or_else(|| dictionary_comparison_coercion62(lhs_type62, rhs_type62, true))
    841
    474
            .or_else(|| ree_comparison_coercion62(lhs_type62, rhs_type62, true))
    842
    474
            .or_else(|| temporal_coercion_nonstrict_timezone62(lhs_type62, rhs_type62))
    843
    474
            .or_else(|| string_coercion62(lhs_type62, rhs_type62))
    844
    474
            .or_else(|| list_coercion46(lhs_type46, rhs_type46))
    845
    474
            .or_else(|| null_coercion46(lhs_type46, rhs_type46))
    846
    474
            .or_else(|| string_numeric_coercion16(lhs_type16, rhs_type16))
    847
    474
            .or_else(|| string_temporal_coercion16(lhs_type16, rhs_type16))
    848
    474
            .or_else(|| binary_coercion16(lhs_type16, rhs_type16))
    849
    474
            .or_else(|| struct_coercion0(lhs_type0, rhs_type0))
    850
    474
            .or_else(|| map_coercion0(lhs_type0, rhs_type0))
    851
    648
    }
    852
    853
    /// Similar to [`comparison_coercion`] but prefers numeric if compares with
    854
    /// numeric and string
    855
    ///
    856
    /// # Numeric comparisons
    857
    ///
    858
    /// When comparing numeric values and strings, the values will be coerced to the
    859
    /// numeric type.  For example, `'2' > 1` if `1` is an `Int32`, the arguments
    860
    /// will be coerced to `Int32`.
    861
    0
    pub fn comparison_coercion_numeric(
    862
    0
        lhs_type: &DataType,
    863
    0
        rhs_type: &DataType,
    864
    0
    ) -> Option<DataType> {
    865
    0
        if lhs_type == rhs_type {
    866
            // same type => equality is possible
    867
    0
            return Some(lhs_type.clone());
    868
    0
        }
    869
    0
        binary_numeric_coercion(lhs_type, rhs_type)
    870
    0
            .or_else(|| dictionary_comparison_coercion_numeric(lhs_type, rhs_type, true))
    871
    0
            .or_else(|| ree_comparison_coercion_numeric(lhs_type, rhs_type, true))
    872
    0
            .or_else(|| string_coercion(lhs_type, rhs_type))
    873
    0
            .or_else(|| null_coercion(lhs_type, rhs_type))
    874
    0
            .or_else(|| string_numeric_coercion_as_numeric(lhs_type, rhs_type))
    875
    0
    }
    876
    877
    /// Coerce `lhs_type` and `rhs_type` to a common type for the purposes of a comparison operation
    878
    /// where one is numeric and one is `Utf8`/`LargeUtf8`.
    879
    16
    fn string_numeric_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    880
        use arrow::datatypes::DataType::*;
    881
    16
        match (lhs_type, rhs_type) {
    882
    0
            (Utf8, _) if rhs_type.is_numeric() => Some(Utf8),
    883
    0
            (LargeUtf8, _) if rhs_type.is_numeric() => Some(LargeUtf8),
    884
    0
            (Utf8View, _) if rhs_type.is_numeric() => Some(Utf8View),
    885
    0
            (_, Utf8) if lhs_type.is_numeric() => Some(Utf8),
    886
    0
            (_, LargeUtf8) if lhs_type.is_numeric() => Some(LargeUtf8),
    887
    0
            (_, Utf8View) if lhs_type.is_numeric() => Some(Utf8View),
    888
    16
            _ => None,
    889
        }
    890
    16
    }
    891
    892
    /// Coerce `lhs_type` and `rhs_type` to a common type for the purposes of a comparison operation
    893
    /// where one is numeric and one is `Utf8`/`LargeUtf8`.
    894
    0
    fn string_numeric_coercion_as_numeric(
    895
    0
        lhs_type: &DataType,
    896
    0
        rhs_type: &DataType,
    897
    0
    ) -> Option<DataType> {
    898
    0
        let lhs_logical_type = NativeType::from(lhs_type);
    899
    0
        let rhs_logical_type = NativeType::from(rhs_type);
    900
    0
        if lhs_logical_type.is_numeric() && rhs_logical_type == NativeType::String {
    901
    0
            return Some(lhs_type.to_owned());
    902
    0
        }
    903
    0
        if rhs_logical_type.is_numeric() && lhs_logical_type == NativeType::String {
    904
    0
            return Some(rhs_type.to_owned());
    905
    0
        }
    906
    907
    0
        None
    908
    0
    }
    909
    910
    /// Coerce `lhs_type` and `rhs_type` to a common type for the purposes of a comparison operation
    911
    /// where one is temporal and one is `Utf8View`/`Utf8`/`LargeUtf8`.
    912
    ///
    913
    /// Note this cannot be performed in case of arithmetic as there is insufficient information
    914
    /// to correctly determine the type of argument. Consider
    915
    ///
    916
    /// ```sql
    917
    /// timestamp > now() - '1 month'
    918
    /// interval > now() - '1970-01-2021'
    919
    /// ```
    920
    ///
    921
    /// In the absence of a full type inference system, we can't determine the correct type
    922
    /// to parse the string argument
    923
    16
    fn string_temporal_coercion(
    924
    16
        lhs_type: &DataType,
    925
    16
        rhs_type: &DataType,
    926
    16
    ) -> Option<DataType> {
    927
        use arrow::datatypes::DataType::*;
    928
    929
    32
        fn match_rule(l: &DataType, r: &DataType) -> Option<DataType> {
    930
    32
            match (l, r) {
    931
                // Coerce Utf8View/Utf8/LargeUtf8 to Date32/Date64/Time32/Time64/Timestamp
    932
    0
                (Utf8, temporal) | (LargeUtf8, temporal) | (Utf8View, temporal) => {
    933
    0
                    match temporal {
    934
    0
                        Date32 | Date64 => Some(temporal.clone()),
    935
                        Time32(_) | Time64(_) => {
    936
    0
                            if is_time_with_valid_unit(temporal) {
    937
    0
                                Some(temporal.to_owned())
    938
                            } else {
    939
    0
                                None
    940
                            }
    941
                        }
    942
    0
                        Timestamp(_, tz) => Some(Timestamp(TimeUnit::Nanosecond, tz.clone())),
    943
    0
                        _ => None,
    944
                    }
    945
                }
    946
    32
                _ => None,
    947
            }
    948
    32
        }
    949
    950
    16
        match_rule(lhs_type, rhs_type).or_else(|| match_rule(rhs_type, lhs_type))
    951
    16
    }
    952
    953
    /// Coerce `lhs_type` and `rhs_type` to a common type where both are numeric
    954
    474
    pub fn binary_numeric_coercion(
    955
    474
        lhs_type: &DataType,
    956
    474
        rhs_type: &DataType,
    957
    474
    ) -> Option<DataType> {
    958
    474
        if !lhs_type.is_numeric() || !rhs_type.is_numeric()434 {
    959
    62
            return None;
    960
    412
        };
    961
    962
        // same type => all good
    963
    412
        if lhs_type == rhs_type {
    964
    0
            return Some(lhs_type.clone());
    965
    412
        }
    966
    967
    412
        if let Some(t) = decimal_coercion(lhs_type, rhs_type) {
    968
    412
            return Some(t);
    969
    0
        }
    970
    971
    0
        numerical_coercion(lhs_type, rhs_type)
    972
    474
    }
    973
    974
    /// Decimal coercion rules.
    975
    412
    pub fn decimal_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    976
        use arrow::datatypes::DataType::*;
    977
    978
        // Prefer decimal data type over floating point for comparison operation
    979
    412
        match (lhs_type, rhs_type) {
    980
            // Same decimal types
    981
    0
            (lhs_type, rhs_type)
    982
    412
                if is_decimal(lhs_type)
    983
    412
                    && is_decimal(rhs_type)
    984
    0
                    && std::mem::discriminant(lhs_type)
    985
    0
                        == std::mem::discriminant(rhs_type) =>
    986
            {
    987
    0
                get_wider_decimal_type(lhs_type, rhs_type)
    988
            }
    989
            // Mismatched decimal types
    990
    0
            (lhs_type, rhs_type)
    991
    412
                if is_decimal(lhs_type)
    992
    412
                    && is_decimal(rhs_type)
    993
    0
                    && std::mem::discriminant(lhs_type)
    994
    0
                        != std::mem::discriminant(rhs_type) =>
    995
            {
    996
    0
                get_wider_decimal_type_cross_variant(lhs_type, rhs_type)
    997
            }
    998
            // Decimal + non-decimal types
    999
            (Decimal32(_, _) | Decimal64(_, _) | Decimal128(_, _) | Decimal256(_, _), _) => {
    1000
    412
                get_common_decimal_type(lhs_type, rhs_type)
    1001
            }
    1002
            (_, Decimal32(_, _) | Decimal64(_, _) | Decimal128(_, _) | Decimal256(_, _)) => {
    1003
    0
                get_common_decimal_type(rhs_type, lhs_type)
    1004
            }
    1005
    0
            (_, _) => None,
    1006
        }
    1007
    412
    }
    1008
    /// Handle cross-variant decimal widening by choosing the larger variant
    1009
    0
    fn get_wider_decimal_type_cross_variant(
    1010
    0
        lhs_type: &DataType,
    1011
    0
        rhs_type: &DataType,
    1012
    0
    ) -> Option<DataType> {
    1013
        use arrow::datatypes::DataType::*;
    1014
    1015
    0
        let (p1, s1) = match lhs_type {
    1016
    0
            Decimal32(p, s) => (*p, *s),
    1017
    0
            Decimal64(p, s) => (*p, *s),
    1018
    0
            Decimal128(p, s) => (*p, *s),
    1019
    0
            Decimal256(p, s) => (*p, *s),
    1020
    0
            _ => return None,
    1021
        };
    1022
    1023
    0
        let (p2, s2) = match rhs_type {
    1024
    0
            Decimal32(p, s) => (*p, *s),
    1025
    0
            Decimal64(p, s) => (*p, *s),
    1026
    0
            Decimal128(p, s) => (*p, *s),
    1027
    0
            Decimal256(p, s) => (*p, *s),
    1028
    0
            _ => return None,
    1029
        };
    1030
    1031
        // max(s1, s2) + max(p1-s1, p2-s2), max(s1, s2)
    1032
    0
        let s = s1.max(s2);
    1033
    0
        let range = (p1 as i8 - s1).max(p2 as i8 - s2);
    1034
    0
        let required_precision = (range + s) as u8;
    1035
    1036
        // Choose the larger variant between the two input types, while making sure we don't overflow the precision.
    1037
    0
        match (lhs_type, rhs_type) {
    1038
            (Decimal32(_, _), Decimal64(_, _)) | (Decimal64(_, _), Decimal32(_, _))
    1039
    0
                if required_precision <= DECIMAL64_MAX_PRECISION =>
    1040
            {
    1041
    0
                Some(Decimal64(required_precision, s))
    1042
            }
    1043
            (Decimal32(_, _), Decimal128(_, _))
    1044
            | (Decimal128(_, _), Decimal32(_, _))
    1045
            | (Decimal64(_, _), Decimal128(_, _))
    1046
            | (Decimal128(_, _), Decimal64(_, _))
    1047
    0
                if required_precision <= DECIMAL128_MAX_PRECISION =>
    1048
            {
    1049
    0
                Some(Decimal128(required_precision, s))
    1050
            }
    1051
            (Decimal32(_, _), Decimal256(_, _))
    1052
            | (Decimal256(_, _), Decimal32(_, _))
    1053
            | (Decimal64(_, _), Decimal256(_, _))
    1054
            | (Decimal256(_, _), Decimal64(_, _))
    1055
            | (Decimal128(_, _), Decimal256(_, _))
    1056
            | (Decimal256(_, _), Decimal128(_, _))
    1057
    0
                if required_precision <= DECIMAL256_MAX_PRECISION =>
    1058
            {
    1059
    0
                Some(Decimal256(required_precision, s))
    1060
            }
    1061
    0
            _ => None,
    1062
        }
    1063
    0
    }
    1064
    1065
    /// Coerce `lhs_type` and `rhs_type` to a common type.
    1066
    412
    fn get_common_decimal_type(
    1067
    412
        decimal_type: &DataType,
    1068
    412
        other_type: &DataType,
    1069
    412
    ) -> Option<DataType> {
    1070
        use arrow::datatypes::DataType::*;
    1071
    412
        match decimal_type {
    1072
            Decimal32(_, _) => {
    1073
    0
                let other_decimal_type = coerce_numeric_type_to_decimal32(other_type)?;
    1074
    0
                get_wider_decimal_type(decimal_type, &other_decimal_type)
    1075
            }
    1076
            Decimal64(_, _) => {
    1077
    0
                let other_decimal_type = coerce_numeric_type_to_decimal64(other_type)?;
    1078
    0
                get_wider_decimal_type(decimal_type, &other_decimal_type)
    1079
            }
    1080
            Decimal128(_, _) => {
    1081
    412
                let other_decimal_type = coerce_numeric_type_to_decimal128(other_type)?0;
    1082
    412
                get_wider_decimal_type(decimal_type, &other_decimal_type)
    1083
            }
    1084
            Decimal256(_, _) => {
    1085
    0
                let other_decimal_type = coerce_numeric_type_to_decimal256(other_type)?;
    1086
    0
                get_wider_decimal_type(decimal_type, &other_decimal_type)
    1087
            }
    1088
    0
            _ => None,
    1089
        }
    1090
    412
    }
    1091
    1092
    /// Returns a decimal [`DataType`] variant that can store any value from either
    1093
    /// `lhs_decimal_type` and `rhs_decimal_type`
    1094
    ///
    1095
    /// The result decimal type is `(max(s1, s2) + max(p1-s1, p2-s2), max(s1, s2))`.
    1096
    412
    fn get_wider_decimal_type(
    1097
    412
        lhs_decimal_type: &DataType,
    1098
    412
        rhs_type: &DataType,
    1099
    412
    ) -> Option<DataType> {
    1100
    412
        match (lhs_decimal_type, rhs_type) {
    1101
    0
            (DataType::Decimal32(p1, s1), DataType::Decimal32(p2, s2)) => {
    1102
                // max(s1, s2) + max(p1-s1, p2-s2), max(s1, s2)
    1103
    0
                let s = *s1.max(s2);
    1104
    0
                let range = (*p1 as i8 - s1).max(*p2 as i8 - s2);
    1105
    0
                Some(create_decimal32_type((range + s) as u8, s))
    1106
            }
    1107
    0
            (DataType::Decimal64(p1, s1), DataType::Decimal64(p2, s2)) => {
    1108
                // max(s1, s2) + max(p1-s1, p2-s2), max(s1, s2)
    1109
    0
                let s = *s1.max(s2);
    1110
    0
                let range = (*p1 as i8 - s1).max(*p2 as i8 - s2);
    1111
    0
                Some(create_decimal64_type((range + s) as u8, s))
    1112
            }
    1113
    412
            (DataType::Decimal128(p1, s1), DataType::Decimal128(p2, s2)) => {
    1114
                // max(s1, s2) + max(p1-s1, p2-s2), max(s1, s2)
    1115
    412
                let s = *s1.max(s2);
    1116
    412
                let range = (*p1 as i8 - s1).max(*p2 as i8 - s2);
    1117
    412
                Some(create_decimal128_type((range + s) as u8, s))
    1118
            }
    1119
    0
            (DataType::Decimal256(p1, s1), DataType::Decimal256(p2, s2)) => {
    1120
                // max(s1, s2) + max(p1-s1, p2-s2), max(s1, s2)
    1121
    0
                let s = *s1.max(s2);
    1122
    0
                let range = (*p1 as i8 - s1).max(*p2 as i8 - s2);
    1123
    0
                Some(create_decimal256_type((range + s) as u8, s))
    1124
            }
    1125
    0
            (_, _) => None,
    1126
        }
    1127
    412
    }
    1128
    1129
    /// Convert the numeric data type to the decimal data type.
    1130
    /// We support signed and unsigned integer types and floating-point type.
    1131
    0
    fn coerce_numeric_type_to_decimal32(numeric_type: &DataType) -> Option<DataType> {
    1132
        use arrow::datatypes::DataType::*;
    1133
        // This conversion rule is from spark
    1134
        // https://github.com/apache/spark/blob/1c81ad20296d34f137238dadd67cc6ae405944eb/sql/catalyst/src/main/scala/org/apache/spark/sql/types/DecimalType.scala#L127
    1135
    0
        match numeric_type {
    1136
    0
            Int8 | UInt8 => Some(Decimal32(3, 0)),
    1137
    0
            Int16 | UInt16 => Some(Decimal32(5, 0)),
    1138
            // TODO if we convert the floating-point data to the decimal type, it maybe overflow.
    1139
    0
            Float16 => Some(Decimal32(6, 3)),
    1140
    0
            _ => None,
    1141
        }
    1142
    0
    }
    1143
    1144
    /// Convert the numeric data type to the decimal data type.
    1145
    /// We support signed and unsigned integer types and floating-point type.
    1146
    0
    fn coerce_numeric_type_to_decimal64(numeric_type: &DataType) -> Option<DataType> {
    1147
        use arrow::datatypes::DataType::*;
    1148
        // This conversion rule is from spark
    1149
        // https://github.com/apache/spark/blob/1c81ad20296d34f137238dadd67cc6ae405944eb/sql/catalyst/src/main/scala/org/apache/spark/sql/types/DecimalType.scala#L127
    1150
    0
        match numeric_type {
    1151
    0
            Int8 | UInt8 => Some(Decimal64(3, 0)),
    1152
    0
            Int16 | UInt16 => Some(Decimal64(5, 0)),
    1153
    0
            Int32 | UInt32 => Some(Decimal64(10, 0)),
    1154
            // TODO if we convert the floating-point data to the decimal type, it maybe overflow.
    1155
    0
            Float16 => Some(Decimal64(6, 3)),
    1156
    0
            Float32 => Some(Decimal64(14, 7)),
    1157
    0
            _ => None,
    1158
        }
    1159
    0
    }
    1160
    1161
    /// Convert the numeric data type to the decimal data type.
    1162
    /// We support signed and unsigned integer types and floating-point type.
    1163
    412
    fn coerce_numeric_type_to_decimal128(numeric_type: &DataType) -> Option<DataType> {
    1164
        use arrow::datatypes::DataType::*;
    1165
        // This conversion rule is from spark
    1166
        // https://github.com/apache/spark/blob/1c81ad20296d34f137238dadd67cc6ae405944eb/sql/catalyst/src/main/scala/org/apache/spark/sql/types/DecimalType.scala#L127
    1167
    412
        match numeric_type {
    1168
    0
            Int8 | UInt8 => Some(Decimal128(3, 0)),
    1169
    0
            Int16 | UInt16 => Some(Decimal128(5, 0)),
    1170
    410
            Int32 | UInt32 => Some(Decimal128(10, 0)),
    1171
    0
            Int64 | UInt64 => Some(Decimal128(20, 0)),
    1172
            // TODO if we convert the floating-point data to the decimal type, it maybe overflow.
    1173
    0
            Float16 => Some(Decimal128(6, 3)),
    1174
    2
            Float32 => Some(Decimal128(14, 7)),
    1175
    0
            Float64 => Some(Decimal128(30, 15)),
    1176
    0
            _ => None,
    1177
        }
    1178
    412
    }
    1179
    1180
    /// Convert the numeric data type to the decimal data type.
    1181
    /// We support signed and unsigned integer types and floating-point type.
    1182
    0
    fn coerce_numeric_type_to_decimal256(numeric_type: &DataType) -> Option<DataType> {
    1183
        use arrow::datatypes::DataType::*;
    1184
        // This conversion rule is from spark
    1185
        // https://github.com/apache/spark/blob/1c81ad20296d34f137238dadd67cc6ae405944eb/sql/catalyst/src/main/scala/org/apache/spark/sql/types/DecimalType.scala#L127
    1186
    0
        match numeric_type {
    1187
    0
            Int8 | UInt8 => Some(Decimal256(3, 0)),
    1188
    0
            Int16 | UInt16 => Some(Decimal256(5, 0)),
    1189
    0
            Int32 | UInt32 => Some(Decimal256(10, 0)),
    1190
    0
            Int64 | UInt64 => Some(Decimal256(20, 0)),
    1191
            // TODO if we convert the floating-point data to the decimal type, it maybe overflow.
    1192
    0
            Float16 => Some(Decimal256(6, 3)),
    1193
    0
            Float32 => Some(Decimal256(14, 7)),
    1194
    0
            Float64 => Some(Decimal256(30, 15)),
    1195
    0
            _ => None,
    1196
        }
    1197
    0
    }
    1198
    1199
    0
    fn struct_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1200
        use arrow::datatypes::DataType::*;
    1201
    0
        match (lhs_type, rhs_type) {
    1202
    0
            (Struct(lhs_fields), Struct(rhs_fields)) => {
    1203
    0
                if lhs_fields.len() != rhs_fields.len() {
    1204
    0
                    return None;
    1205
    0
                }
    1206
    1207
    0
                let coerced_types = std::iter::zip(lhs_fields.iter(), rhs_fields.iter())
    1208
    0
                    .map(|(lhs, rhs)| comparison_coercion(lhs.data_type(), rhs.data_type()))
    1209
    0
                    .collect::<Option<Vec<DataType>>>()?;
    1210
    1211
                // preserve the field name and nullability
    1212
    0
                let orig_fields = std::iter::zip(lhs_fields.iter(), rhs_fields.iter());
    1213
    1214
    0
                let fields: Vec<FieldRef> = coerced_types
    1215
    0
                    .into_iter()
    1216
    0
                    .zip(orig_fields)
    1217
    0
                    .map(|(datatype, (lhs, rhs))| coerce_fields(datatype, lhs, rhs))
    1218
    0
                    .collect();
    1219
    0
                Some(Struct(fields.into()))
    1220
            }
    1221
    0
            _ => None,
    1222
        }
    1223
    0
    }
    1224
    1225
    /// returns the result of coercing two fields to a common type
    1226
    0
    fn coerce_fields(common_type: DataType, lhs: &FieldRef, rhs: &FieldRef) -> FieldRef {
    1227
    0
        let is_nullable = lhs.is_nullable() || rhs.is_nullable();
    1228
    0
        let name = lhs.name(); // pick the name from the left field
    1229
    0
        Arc::new(Field::new(name, common_type, is_nullable))
    1230
    0
    }
    1231
    1232
    /// coerce two types if they are Maps by coercing their inner 'entries' fields' types
    1233
    /// using struct coercion
    1234
    0
    fn map_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1235
        use arrow::datatypes::DataType::*;
    1236
    0
        match (lhs_type, rhs_type) {
    1237
    0
            (Map(lhs_field, lhs_ordered), Map(rhs_field, rhs_ordered)) => {
    1238
    0
                struct_coercion(lhs_field.data_type(), rhs_field.data_type()).map(
    1239
    0
                    |key_value_type| {
    1240
                        Map(
    1241
    0
                            Arc::new((**lhs_field).clone().with_data_type(key_value_type)),
    1242
    0
                            *lhs_ordered && *rhs_ordered,
    1243
                        )
    1244
    0
                    },
    1245
                )
    1246
            }
    1247
    0
            _ => None,
    1248
        }
    1249
    0
    }
    1250
    1251
    /// Returns the output type of applying mathematics operations such as
    1252
    /// `+` to arguments of `lhs_type` and `rhs_type`.
    1253
    0
    fn mathematics_numerical_coercion(
    1254
    0
        lhs_type: &DataType,
    1255
    0
        rhs_type: &DataType,
    1256
    0
    ) -> Option<DataType> {
    1257
        use arrow::datatypes::DataType::*;
    1258
    1259
        // Error on any non-numeric type
    1260
    0
        if !both_numeric_or_null_and_numeric(lhs_type, rhs_type) {
    1261
    0
            return None;
    1262
    0
        };
    1263
    1264
        // These are ordered from most informative to least informative so
    1265
        // that the coercion removes the least amount of information
    1266
    0
        match (lhs_type, rhs_type) {
    1267
    0
            (Dictionary(_, lhs_value_type), Dictionary(_, rhs_value_type)) => {
    1268
    0
                mathematics_numerical_coercion(lhs_value_type, rhs_value_type)
    1269
            }
    1270
    0
            (Dictionary(_, value_type), _) => {
    1271
    0
                mathematics_numerical_coercion(value_type, rhs_type)
    1272
            }
    1273
    0
            (_, Dictionary(_, value_type)) => {
    1274
    0
                mathematics_numerical_coercion(lhs_type, value_type)
    1275
            }
    1276
    0
            _ => numerical_coercion(lhs_type, rhs_type),
    1277
        }
    1278
    0
    }
    1279
    1280
    /// A common set of numerical coercions that are applied for mathematical and binary ops
    1281
    /// to `lhs_type` and `rhs_type`.
    1282
    0
    fn numerical_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1283
        use arrow::datatypes::DataType::*;
    1284
    1285
    0
        match (lhs_type, rhs_type) {
    1286
    0
            (Float64, _) | (_, Float64) => Some(Float64),
    1287
    0
            (_, Float32) | (Float32, _) => Some(Float32),
    1288
    0
            (_, Float16) | (Float16, _) => Some(Float16),
    1289
            // The following match arms encode the following logic: Given the two
    1290
            // integral types, we choose the narrowest possible integral type that
    1291
            // accommodates all values of both types. Note that to avoid information
    1292
            // loss when combining UInt64 with signed integers we use Decimal128(20, 0).
    1293
            (UInt64, Int64 | Int32 | Int16 | Int8)
    1294
    0
            | (Int64 | Int32 | Int16 | Int8, UInt64) => Some(Decimal128(20, 0)),
    1295
    0
            (UInt64, _) | (_, UInt64) => Some(UInt64),
    1296
            (Int64, _)
    1297
            | (_, Int64)
    1298
            | (UInt32, Int32 | Int16 | Int8)
    1299
    0
            | (Int32 | Int16 | Int8, UInt32) => Some(Int64),
    1300
    0
            (UInt32, _) | (_, UInt32) => Some(UInt32),
    1301
            (Int32, _) | (_, Int32) | (UInt16, Int16 | Int8) | (Int16 | Int8, UInt16) => {
    1302
    0
                Some(Int32)
    1303
            }
    1304
    0
            (UInt16, _) | (_, UInt16) => Some(UInt16),
    1305
    0
            (Int16, _) | (_, Int16) | (Int8, UInt8) | (UInt8, Int8) => Some(Int16),
    1306
    0
            (Int8, _) | (_, Int8) => Some(Int8),
    1307
    0
            (UInt8, _) | (_, UInt8) => Some(UInt8),
    1308
    0
            _ => None,
    1309
        }
    1310
    0
    }
    1311
    1312
    0
    fn create_decimal32_type(precision: u8, scale: i8) -> DataType {
    1313
    0
        DataType::Decimal32(
    1314
    0
            DECIMAL32_MAX_PRECISION.min(precision),
    1315
    0
            DECIMAL32_MAX_SCALE.min(scale),
    1316
    0
        )
    1317
    0
    }
    1318
    1319
    0
    fn create_decimal64_type(precision: u8, scale: i8) -> DataType {
    1320
    0
        DataType::Decimal64(
    1321
    0
            DECIMAL64_MAX_PRECISION.min(precision),
    1322
    0
            DECIMAL64_MAX_SCALE.min(scale),
    1323
    0
        )
    1324
    0
    }
    1325
    1326
    412
    fn create_decimal128_type(precision: u8, scale: i8) -> DataType {
    1327
    412
        DataType::Decimal128(
    1328
    412
            DECIMAL128_MAX_PRECISION.min(precision),
    1329
    412
            DECIMAL128_MAX_SCALE.min(scale),
    1330
    412
        )
    1331
    412
    }
    1332
    1333
    0
    fn create_decimal256_type(precision: u8, scale: i8) -> DataType {
    1334
    0
        DataType::Decimal256(
    1335
    0
            DECIMAL256_MAX_PRECISION.min(precision),
    1336
    0
            DECIMAL256_MAX_SCALE.min(scale),
    1337
    0
        )
    1338
    0
    }
    1339
    1340
    /// Determine if at least of one of lhs and rhs is numeric, and the other must be NULL or numeric
    1341
    0
    fn both_numeric_or_null_and_numeric(lhs_type: &DataType, rhs_type: &DataType) -> bool {
    1342
        use arrow::datatypes::DataType::*;
    1343
    0
        match (lhs_type, rhs_type) {
    1344
    0
            (_, Null) => lhs_type.is_numeric(),
    1345
    0
            (Null, _) => rhs_type.is_numeric(),
    1346
    0
            (Dictionary(_, lhs_value_type), Dictionary(_, rhs_value_type)) => {
    1347
    0
                lhs_value_type.is_numeric() && rhs_value_type.is_numeric()
    1348
            }
    1349
    0
            (Dictionary(_, value_type), _) => {
    1350
    0
                value_type.is_numeric() && rhs_type.is_numeric()
    1351
            }
    1352
    0
            (_, Dictionary(_, value_type)) => {
    1353
    0
                lhs_type.is_numeric() && value_type.is_numeric()
    1354
            }
    1355
    0
            _ => lhs_type.is_numeric() && rhs_type.is_numeric(),
    1356
        }
    1357
    0
    }
    1358
    1359
    /// Generic coercion rules for Dictionaries: the type that both lhs and rhs
    1360
    /// can be casted to for the purpose of a computation.
    1361
    ///
    1362
    /// Not all operators support dictionaries, if `preserve_dictionaries` is true
    1363
    /// dictionaries will be preserved if possible.
    1364
    ///
    1365
    /// The `coerce_fn` parameter determines which comparison coercion function to use
    1366
    /// for comparing the dictionary value types.
    1367
    62
    fn dictionary_comparison_coercion_generic(
    1368
    62
        lhs_type: &DataType,
    1369
    62
        rhs_type: &DataType,
    1370
    62
        preserve_dictionaries: bool,
    1371
    62
        coerce_fn: fn(&DataType, &DataType) -> Option<DataType>,
    1372
    62
    ) -> Option<DataType> {
    1373
        use arrow::datatypes::DataType::*;
    1374
    62
        match (lhs_type, rhs_type) {
    1375
            (
    1376
    0
                Dictionary(_lhs_index_type, lhs_value_type),
    1377
    0
                Dictionary(_rhs_index_type, rhs_value_type),
    1378
    0
            ) => coerce_fn(lhs_value_type, rhs_value_type),
    1379
    0
            (d @ Dictionary(_, value_type), other_type)
    1380
    0
            | (other_type, d @ Dictionary(_, value_type))
    1381
    0
                if preserve_dictionaries && value_type.as_ref() == other_type =>
    1382
            {
    1383
    0
                Some(d.clone())
    1384
            }
    1385
    0
            (Dictionary(_index_type, value_type), _) => coerce_fn(value_type, rhs_type),
    1386
    0
            (_, Dictionary(_index_type, value_type)) => coerce_fn(lhs_type, value_type),
    1387
    62
            _ => None,
    1388
        }
    1389
    62
    }
    1390
    1391
    /// Coercion rules for Dictionaries: the type that both lhs and rhs
    1392
    /// can be casted to for the purpose of a computation.
    1393
    ///
    1394
    /// Not all operators support dictionaries, if `preserve_dictionaries` is true
    1395
    /// dictionaries will be preserved if possible
    1396
    62
    fn dictionary_comparison_coercion(
    1397
    62
        lhs_type: &DataType,
    1398
    62
        rhs_type: &DataType,
    1399
    62
        preserve_dictionaries: bool,
    1400
    62
    ) -> Option<DataType> {
    1401
    62
        dictionary_comparison_coercion_generic(
    1402
    62
            lhs_type,
    1403
    62
            rhs_type,
    1404
    62
            preserve_dictionaries,
    1405
    62
            comparison_coercion,
    1406
        )
    1407
    62
    }
    1408
    1409
    /// Coercion rules for Dictionaries with numeric preference: similar to
    1410
    /// [`dictionary_comparison_coercion`] but uses [`comparison_coercion_numeric`]
    1411
    /// which prefers numeric types over strings when both are present.
    1412
    ///
    1413
    /// This is used by [`comparison_coercion_numeric`] to maintain consistent
    1414
    /// numeric-preferring semantics when dealing with dictionary types.
    1415
    0
    fn dictionary_comparison_coercion_numeric(
    1416
    0
        lhs_type: &DataType,
    1417
    0
        rhs_type: &DataType,
    1418
    0
        preserve_dictionaries: bool,
    1419
    0
    ) -> Option<DataType> {
    1420
    0
        dictionary_comparison_coercion_generic(
    1421
    0
            lhs_type,
    1422
    0
            rhs_type,
    1423
    0
            preserve_dictionaries,
    1424
    0
            comparison_coercion_numeric,
    1425
        )
    1426
    0
    }
    1427
    1428
    /// Coercion rules for RunEndEncoded: the type that both lhs and rhs
    1429
    /// can be casted to for the purpose of a computation.
    1430
    ///
    1431
    /// Not all operators support REE, if `preserve_ree` is true
    1432
    /// REE will be preserved if possible
    1433
    ///
    1434
    /// The `coerce_fn` parameter determines which comparison coercion function to use
    1435
    /// for comparing the REE value types.
    1436
    62
    fn ree_comparison_coercion_generic(
    1437
    62
        lhs_type: &DataType,
    1438
    62
        rhs_type: &DataType,
    1439
    62
        preserve_ree: bool,
    1440
    62
        coerce_fn: fn(&DataType, &DataType) -> Option<DataType>,
    1441
    62
    ) -> Option<DataType> {
    1442
        use arrow::datatypes::DataType::*;
    1443
    62
        match (lhs_type, rhs_type) {
    1444
    0
            (RunEndEncoded(_, lhs_values_field), RunEndEncoded(_, rhs_values_field)) => {
    1445
    0
                coerce_fn(lhs_values_field.data_type(), rhs_values_field.data_type())
    1446
            }
    1447
    0
            (ree @ RunEndEncoded(_, values_field), other_type)
    1448
    0
            | (other_type, ree @ RunEndEncoded(_, values_field))
    1449
    0
                if preserve_ree && values_field.data_type() == other_type =>
    1450
            {
    1451
    0
                Some(ree.clone())
    1452
            }
    1453
    0
            (RunEndEncoded(_, values_field), _) => {
    1454
    0
                coerce_fn(values_field.data_type(), rhs_type)
    1455
            }
    1456
    0
            (_, RunEndEncoded(_, values_field)) => {
    1457
    0
                coerce_fn(lhs_type, values_field.data_type())
    1458
            }
    1459
    62
            _ => None,
    1460
        }
    1461
    62
    }
    1462
    1463
    /// Coercion rules for RunEndEncoded: the type that both lhs and rhs
    1464
    /// can be casted to for the purpose of a computation.
    1465
    ///
    1466
    /// Not all operators support REE, if `preserve_ree` is true
    1467
    /// REE will be preserved if possible
    1468
    62
    fn ree_comparison_coercion(
    1469
    62
        lhs_type: &DataType,
    1470
    62
        rhs_type: &DataType,
    1471
    62
        preserve_ree: bool,
    1472
    62
    ) -> Option<DataType> {
    1473
    62
        ree_comparison_coercion_generic(lhs_type, rhs_type, preserve_ree, comparison_coercion)
    1474
    62
    }
    1475
    1476
    /// Coercion rules for RunEndEncoded with numeric preference: similar to
    1477
    /// [`ree_comparison_coercion`] but uses [`comparison_coercion_numeric`]
    1478
    /// which prefers numeric types over strings when both are present.
    1479
    ///
    1480
    /// This is used by [`comparison_coercion_numeric`] to maintain consistent
    1481
    /// numeric-preferring semantics when dealing with REE types.
    1482
    0
    fn ree_comparison_coercion_numeric(
    1483
    0
        lhs_type: &DataType,
    1484
    0
        rhs_type: &DataType,
    1485
    0
        preserve_ree: bool,
    1486
    0
    ) -> Option<DataType> {
    1487
    0
        ree_comparison_coercion_generic(
    1488
    0
            lhs_type,
    1489
    0
            rhs_type,
    1490
    0
            preserve_ree,
    1491
    0
            comparison_coercion_numeric,
    1492
        )
    1493
    0
    }
    1494
    1495
    /// Coercion rules for string concat.
    1496
    /// This is a union of string coercion rules and specified rules:
    1497
    /// 1. At least one side of lhs and rhs should be string type (Utf8 / LargeUtf8)
    1498
    /// 2. Data type of the other side should be able to cast to string type
    1499
    0
    fn string_concat_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1500
        use arrow::datatypes::DataType::*;
    1501
    0
        string_coercion(lhs_type, rhs_type).or_else(|| match (lhs_type, rhs_type) {
    1502
    0
            (Utf8View, from_type) | (from_type, Utf8View) => {
    1503
    0
                string_concat_internal_coercion(from_type, &Utf8View)
    1504
            }
    1505
    0
            (Utf8, from_type) | (from_type, Utf8) => {
    1506
    0
                string_concat_internal_coercion(from_type, &Utf8)
    1507
            }
    1508
    0
            (LargeUtf8, from_type) | (from_type, LargeUtf8) => {
    1509
    0
                string_concat_internal_coercion(from_type, &LargeUtf8)
    1510
            }
    1511
    0
            (Dictionary(_, lhs_value_type), Dictionary(_, rhs_value_type)) => {
    1512
    0
                string_coercion(lhs_value_type, rhs_value_type).or(None)
    1513
            }
    1514
    0
            _ => None,
    1515
    0
        })
    1516
    0
    }
    1517
    1518
    0
    fn array_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1519
    0
        if lhs_type.equals_datatype(rhs_type) {
    1520
    0
            Some(lhs_type.to_owned())
    1521
        } else {
    1522
    0
            None
    1523
        }
    1524
    0
    }
    1525
    1526
    /// If `from_type` can be casted to `to_type`, return `to_type`, otherwise
    1527
    /// return `None`.
    1528
    0
    fn string_concat_internal_coercion(
    1529
    0
        from_type: &DataType,
    1530
    0
        to_type: &DataType,
    1531
    0
    ) -> Option<DataType> {
    1532
    0
        if can_cast_types(from_type, to_type) {
    1533
    0
            Some(to_type.to_owned())
    1534
        } else {
    1535
    0
            None
    1536
        }
    1537
    0
    }
    1538
    1539
    /// Coercion rules for string view types (Utf8/LargeUtf8/Utf8View):
    1540
    /// If at least one argument is a string view, we coerce to string view
    1541
    /// based on the observation that StringArray to StringViewArray is cheap but not vice versa.
    1542
    ///
    1543
    /// Between Utf8 and LargeUtf8, we coerce to LargeUtf8.
    1544
    62
    pub fn string_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1545
        use arrow::datatypes::DataType::*;
    1546
    62
        match (lhs_type, rhs_type) {
    1547
            // If Utf8View is in any side, we coerce to Utf8View.
    1548
            (Utf8View, Utf8View | Utf8 | LargeUtf8) | (Utf8 | LargeUtf8, Utf8View) => {
    1549
    8
                Some(Utf8View)
    1550
            }
    1551
            // Then, if LargeUtf8 is in any side, we coerce to LargeUtf8.
    1552
    8
            (LargeUtf8, Utf8 | LargeUtf8) | (Utf8, LargeUtf8) => Some(LargeUtf8),
    1553
            // Utf8 coerces to Utf8
    1554
    0
            (Utf8, Utf8) => Some(Utf8),
    1555
    46
            _ => None,
    1556
        }
    1557
    62
    }
    1558
    1559
    0
    fn numeric_string_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1560
        use arrow::datatypes::DataType::*;
    1561
    0
        match (lhs_type, rhs_type) {
    1562
    0
            (Utf8 | LargeUtf8 | Utf8View, other_type)
    1563
    0
            | (other_type, Utf8 | LargeUtf8 | Utf8View)
    1564
    0
                if other_type.is_numeric() =>
    1565
            {
    1566
    0
                Some(other_type.clone())
    1567
            }
    1568
    0
            _ => None,
    1569
        }
    1570
    0
    }
    1571
    1572
    /// Coerces two fields together, ensuring the field data (name and nullability) is correctly set.
    1573
    0
    fn coerce_list_children(lhs_field: &FieldRef, rhs_field: &FieldRef) -> Option<FieldRef> {
    1574
    0
        let data_types = vec![lhs_field.data_type().clone(), rhs_field.data_type().clone()];
    1575
    0
        Some(Arc::new(
    1576
    0
            (**lhs_field)
    1577
    0
                .clone()
    1578
    0
                .with_data_type(type_union_resolution(&data_types)?)
    1579
    0
                .with_nullable(lhs_field.is_nullable() || rhs_field.is_nullable()),
    1580
        ))
    1581
    0
    }
    1582
    1583
    /// Coercion rules for list types.
    1584
    46
    fn list_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1585
        use arrow::datatypes::DataType::*;
    1586
    46
        match (lhs_type, rhs_type) {
    1587
            // Coerce to the left side FixedSizeList type if the list lengths are the same,
    1588
            // otherwise coerce to list with the left type for dynamic length
    1589
    0
            (FixedSizeList(lhs_field, ls), FixedSizeList(rhs_field, rs)) => {
    1590
    0
                if ls == rs {
    1591
                    Some(FixedSizeList(
    1592
    0
                        coerce_list_children(lhs_field, rhs_field)?,
    1593
    0
                        *rs,
    1594
                    ))
    1595
                } else {
    1596
    0
                    Some(List(coerce_list_children(lhs_field, rhs_field)?))
    1597
                }
    1598
            }
    1599
            // LargeList on any side
    1600
            (
    1601
    0
                LargeList(lhs_field),
    1602
    0
                List(rhs_field) | LargeList(rhs_field) | FixedSizeList(rhs_field, _),
    1603
            )
    1604
    0
            | (List(lhs_field) | FixedSizeList(lhs_field, _), LargeList(rhs_field)) => {
    1605
    0
                Some(LargeList(coerce_list_children(lhs_field, rhs_field)?))
    1606
            }
    1607
            // Lists on both sides
    1608
    0
            (List(lhs_field), List(rhs_field) | FixedSizeList(rhs_field, _))
    1609
    0
            | (FixedSizeList(lhs_field, _), List(rhs_field)) => {
    1610
    0
                Some(List(coerce_list_children(lhs_field, rhs_field)?))
    1611
            }
    1612
    46
            _ => None,
    1613
        }
    1614
    46
    }
    1615
    1616
    /// Coercion rules for binary (Binary/LargeBinary) to string (Utf8/LargeUtf8):
    1617
    /// If one argument is binary and the other is a string then coerce to string
    1618
    /// (e.g. for `like`)
    1619
    0
    pub fn binary_to_string_coercion(
    1620
    0
        lhs_type: &DataType,
    1621
    0
        rhs_type: &DataType,
    1622
    0
    ) -> Option<DataType> {
    1623
        use arrow::datatypes::DataType::*;
    1624
    0
        match (lhs_type, rhs_type) {
    1625
    0
            (Binary, Utf8) => Some(Utf8),
    1626
    0
            (Binary, LargeUtf8) => Some(LargeUtf8),
    1627
    0
            (BinaryView, Utf8) => Some(Utf8View),
    1628
    0
            (BinaryView, LargeUtf8) => Some(LargeUtf8),
    1629
    0
            (LargeBinary, Utf8) => Some(LargeUtf8),
    1630
    0
            (LargeBinary, LargeUtf8) => Some(LargeUtf8),
    1631
    0
            (Utf8, Binary) => Some(Utf8),
    1632
    0
            (Utf8, LargeBinary) => Some(LargeUtf8),
    1633
    0
            (Utf8, BinaryView) => Some(Utf8View),
    1634
    0
            (LargeUtf8, Binary) => Some(LargeUtf8),
    1635
    0
            (LargeUtf8, LargeBinary) => Some(LargeUtf8),
    1636
    0
            (LargeUtf8, BinaryView) => Some(LargeUtf8),
    1637
    0
            _ => None,
    1638
        }
    1639
    0
    }
    1640
    1641
    /// Coercion rules for binary types (Binary/LargeBinary/BinaryView): If at least one argument is
    1642
    /// a binary type and both arguments can be coerced into a binary type, coerce
    1643
    /// to binary type.
    1644
    16
    fn binary_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1645
        use arrow::datatypes::DataType::*;
    1646
    16
        match (lhs_type, rhs_type) {
    1647
            // If BinaryView is in any side, we coerce to BinaryView.
    1648
            (BinaryView, BinaryView | Binary | LargeBinary | Utf8 | LargeUtf8 | Utf8View)
    1649
            | (LargeBinary | Binary | Utf8 | LargeUtf8 | Utf8View, BinaryView) => {
    1650
    8
                Some(BinaryView)
    1651
            }
    1652
            // Prefer LargeBinary over Binary
    1653
            (LargeBinary | Binary | Utf8 | LargeUtf8 | Utf8View, LargeBinary)
    1654
    8
            | (LargeBinary, Binary | Utf8 | LargeUtf8 | Utf8View) => Some(LargeBinary),
    1655
    1656
            // If Utf8View/LargeUtf8 presents need to be large Binary
    1657
            (Utf8View | LargeUtf8, Binary) | (Binary, Utf8View | LargeUtf8) => {
    1658
    0
                Some(LargeBinary)
    1659
            }
    1660
    0
            (Binary, Utf8) | (Utf8, Binary) => Some(Binary),
    1661
    1662
            // Cast FixedSizeBinary to Binary
    1663
    0
            (FixedSizeBinary(_), Binary) | (Binary, FixedSizeBinary(_)) => Some(Binary),
    1664
            (FixedSizeBinary(_), BinaryView) | (BinaryView, FixedSizeBinary(_)) => {
    1665
    0
                Some(BinaryView)
    1666
            }
    1667
    1668
    0
            _ => None,
    1669
        }
    1670
    16
    }
    1671
    1672
    /// Coercion rules for like operations.
    1673
    /// This is a union of string coercion rules, dictionary coercion rules, and REE coercion rules
    1674
    0
    pub fn like_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1675
    0
        string_coercion(lhs_type, rhs_type)
    1676
    0
            .or_else(|| list_coercion(lhs_type, rhs_type))
    1677
    0
            .or_else(|| binary_to_string_coercion(lhs_type, rhs_type))
    1678
    0
            .or_else(|| dictionary_comparison_coercion(lhs_type, rhs_type, false))
    1679
    0
            .or_else(|| ree_comparison_coercion(lhs_type, rhs_type, false))
    1680
    0
            .or_else(|| regex_null_coercion(lhs_type, rhs_type))
    1681
    0
            .or_else(|| null_coercion(lhs_type, rhs_type))
    1682
    0
    }
    1683
    1684
    /// Coercion rules for regular expression comparison operations with NULL input.
    1685
    0
    fn regex_null_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1686
        use arrow::datatypes::DataType::*;
    1687
    0
        match (lhs_type, rhs_type) {
    1688
    0
            (Null, Utf8View | Utf8 | LargeUtf8) => Some(rhs_type.clone()),
    1689
    0
            (Utf8View | Utf8 | LargeUtf8, Null) => Some(lhs_type.clone()),
    1690
    0
            (Null, Null) => Some(Utf8),
    1691
    0
            _ => None,
    1692
        }
    1693
    0
    }
    1694
    1695
    /// Coercion rules for regular expression comparison operations.
    1696
    /// This is a union of string coercion rules and dictionary coercion rules
    1697
    0
    pub fn regex_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1698
    0
        string_coercion(lhs_type, rhs_type)
    1699
    0
            .or_else(|| dictionary_comparison_coercion(lhs_type, rhs_type, false))
    1700
    0
            .or_else(|| regex_null_coercion(lhs_type, rhs_type))
    1701
    0
    }
    1702
    1703
    /// Checks if the TimeUnit associated with a Time32 or Time64 type is consistent,
    1704
    /// as Time32 can only be used to Second and Millisecond accuracy, while Time64
    1705
    /// is exclusively used to Microsecond and Nanosecond accuracy
    1706
    0
    fn is_time_with_valid_unit(datatype: &DataType) -> bool {
    1707
    0
        matches!(
    1708
    0
            datatype,
    1709
            &DataType::Time32(TimeUnit::Second)
    1710
                | &DataType::Time32(TimeUnit::Millisecond)
    1711
                | &DataType::Time64(TimeUnit::Microsecond)
    1712
                | &DataType::Time64(TimeUnit::Nanosecond)
    1713
        )
    1714
    0
    }
    1715
    1716
    /// Non-strict Timezone Coercion is useful in scenarios where we can guarantee
    1717
    /// a stable relationship between two timestamps of different timezones.
    1718
    ///
    1719
    /// An example of this is binary comparisons (<, >, ==, etc). Arrow stores timestamps
    1720
    /// as relative to UTC epoch, and then adds the timezone as an offset. As a result, we can always
    1721
    /// do a binary comparison between the two times.
    1722
    ///
    1723
    /// Timezone coercion is handled by the following rules:
    1724
    /// - If only one has a timezone, coerce the other to match
    1725
    /// - If both have a timezone, coerce to the left type
    1726
    /// - "UTC" and "+00:00" are considered equivalent
    1727
    62
    fn temporal_coercion_nonstrict_timezone(
    1728
    62
        lhs_type: &DataType,
    1729
    62
        rhs_type: &DataType,
    1730
    62
    ) -> Option<DataType> {
    1731
        use arrow::datatypes::DataType::*;
    1732
    1733
    62
        match (lhs_type, rhs_type) {
    1734
    0
            (Timestamp(lhs_unit, lhs_tz), Timestamp(rhs_unit, rhs_tz)) => {
    1735
    0
                let tz = match (lhs_tz, rhs_tz) {
    1736
                    // If both have a timezone, use the left timezone.
    1737
    0
                    (Some(lhs_tz), Some(_rhs_tz)) => Some(Arc::clone(lhs_tz)),
    1738
    0
                    (Some(lhs_tz), None) => Some(Arc::clone(lhs_tz)),
    1739
    0
                    (None, Some(rhs_tz)) => Some(Arc::clone(rhs_tz)),
    1740
    0
                    (None, None) => None,
    1741
                };
    1742
    1743
    0
                let unit = timeunit_coercion(lhs_unit, rhs_unit);
    1744
    1745
    0
                Some(Timestamp(unit, tz))
    1746
            }
    1747
    62
            _ => temporal_coercion(lhs_type, rhs_type),
    1748
        }
    1749
    62
    }
    1750
    1751
    /// Strict Timezone coercion is useful in scenarios where we cannot guarantee a stable relationship
    1752
    /// between two timestamps with different timezones or do not want implicit coercion between them.
    1753
    ///
    1754
    /// An example of this when attempting to coerce function arguments. Functions already have a mechanism
    1755
    /// for defining which timestamp types they want to support, so we do not want to do any further coercion.
    1756
    ///
    1757
    /// Coercion rules for Temporal columns: the type that both lhs and rhs can be
    1758
    /// casted to for the purpose of a date computation
    1759
    /// For interval arithmetic, it doesn't handle datetime type +/- interval
    1760
    /// Timezone coercion is handled by the following rules:
    1761
    /// - If only one has a timezone, coerce the other to match
    1762
    /// - If both have a timezone, throw an error
    1763
    /// - "UTC" and "+00:00" are considered equivalent
    1764
    0
    fn temporal_coercion_strict_timezone(
    1765
    0
        lhs_type: &DataType,
    1766
    0
        rhs_type: &DataType,
    1767
    0
    ) -> Option<DataType> {
    1768
        use arrow::datatypes::DataType::*;
    1769
    1770
    0
        match (lhs_type, rhs_type) {
    1771
    0
            (Timestamp(lhs_unit, lhs_tz), Timestamp(rhs_unit, rhs_tz)) => {
    1772
    0
                let tz = match (lhs_tz, rhs_tz) {
    1773
    0
                    (Some(lhs_tz), Some(rhs_tz)) => {
    1774
    0
                        match (lhs_tz.as_ref(), rhs_tz.as_ref()) {
    1775
                            // UTC and "+00:00" are the same by definition. Most other timezones
    1776
                            // do not have a 1-1 mapping between timezone and an offset from UTC
    1777
    0
                            ("UTC", "+00:00") | ("+00:00", "UTC") => Some(Arc::clone(lhs_tz)),
    1778
    0
                            (lhs, rhs) if lhs == rhs => Some(Arc::clone(lhs_tz)),
    1779
                            // can't cast across timezones
    1780
                            _ => {
    1781
    0
                                return None;
    1782
                            }
    1783
                        }
    1784
                    }
    1785
    0
                    (Some(lhs_tz), None) => Some(Arc::clone(lhs_tz)),
    1786
    0
                    (None, Some(rhs_tz)) => Some(Arc::clone(rhs_tz)),
    1787
    0
                    (None, None) => None,
    1788
                };
    1789
    1790
    0
                let unit = timeunit_coercion(lhs_unit, rhs_unit);
    1791
    1792
    0
                Some(Timestamp(unit, tz))
    1793
            }
    1794
    0
            _ => temporal_coercion(lhs_type, rhs_type),
    1795
        }
    1796
    0
    }
    1797
    1798
    62
    fn temporal_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1799
        use arrow::datatypes::DataType::*;
    1800
        use arrow::datatypes::IntervalUnit::*;
    1801
        use arrow::datatypes::TimeUnit::*;
    1802
    1803
    62
        match (lhs_type, rhs_type) {
    1804
            (Interval(_) | Duration(_), Interval(_) | Duration(_)) => {
    1805
    0
                Some(Interval(MonthDayNano))
    1806
            }
    1807
    0
            (Date64, Date32) | (Date32, Date64) => Some(Date64),
    1808
            (Timestamp(_, None), Date64) | (Date64, Timestamp(_, None)) => {
    1809
    0
                Some(Timestamp(Nanosecond, None))
    1810
            }
    1811
    0
            (Timestamp(_, _tz), Date64) | (Date64, Timestamp(_, _tz)) => {
    1812
    0
                Some(Timestamp(Nanosecond, None))
    1813
            }
    1814
            (Timestamp(_, None), Date32) | (Date32, Timestamp(_, None)) => {
    1815
    0
                Some(Timestamp(Nanosecond, None))
    1816
            }
    1817
    0
            (Timestamp(_, _tz), Date32) | (Date32, Timestamp(_, _tz)) => {
    1818
    0
                Some(Timestamp(Nanosecond, None))
    1819
            }
    1820
    62
            _ => None,
    1821
        }
    1822
    62
    }
    1823
    1824
    0
    fn timeunit_coercion(lhs_unit: &TimeUnit, rhs_unit: &TimeUnit) -> TimeUnit {
    1825
        use arrow::datatypes::TimeUnit::*;
    1826
    0
        match (lhs_unit, rhs_unit) {
    1827
    0
            (Second, Millisecond) => Second,
    1828
    0
            (Second, Microsecond) => Second,
    1829
    0
            (Second, Nanosecond) => Second,
    1830
    0
            (Millisecond, Second) => Second,
    1831
    0
            (Millisecond, Microsecond) => Millisecond,
    1832
    0
            (Millisecond, Nanosecond) => Millisecond,
    1833
    0
            (Microsecond, Second) => Second,
    1834
    0
            (Microsecond, Millisecond) => Millisecond,
    1835
    0
            (Microsecond, Nanosecond) => Microsecond,
    1836
    0
            (Nanosecond, Second) => Second,
    1837
    0
            (Nanosecond, Millisecond) => Millisecond,
    1838
    0
            (Nanosecond, Microsecond) => Microsecond,
    1839
    0
            (l, r) => {
    1840
    0
                assert_eq!(l, r);
    1841
    0
                *l
    1842
            }
    1843
        }
    1844
    0
    }
    1845
    1846
    /// Coercion rules from NULL type. Since NULL can be cast to any other type in arrow,
    1847
    /// either lhs or rhs is NULL, if NULL can be cast to type of the other side, the coercion is valid.
    1848
    46
    fn null_coercion(lhs_type: &DataType, rhs_type: &DataType) -> Option<DataType> {
    1849
    46
        match (lhs_type, rhs_type) {
    1850
    28
            (DataType::Null, other_type2) | (other_type, DataType::Null) => {
    1851
    30
                if can_cast_types(&DataType::Null, other_type) {
    1852
    30
                    Some(other_type.clone())
    1853
                } else {
    1854
    0
                    None
    1855
                }
    1856
            }
    1857
    16
            _ => None,
    1858
        }
    1859
    46
    }
    1860
    1861
    #[cfg(test)]
    1862
    mod tests;