## 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/signature.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
    //! Function signatures: [`Volatility`], [`Signature`] and [`TypeSignature`]
    19
    20
    use std::fmt::Display;
    21
    use std::hash::Hash;
    22
    23
    use crate::type_coercion::aggregates::NUMERICS;
    24
    use arrow::datatypes::{DataType, Decimal128Type, DecimalType, IntervalUnit, TimeUnit};
    25
    use datafusion_common::types::{LogicalType, LogicalTypeRef, NativeType};
    26
    use datafusion_common::utils::ListCoercion;
    27
    use datafusion_common::{internal_err, plan_err, Result};
    28
    use indexmap::IndexSet;
    29
    use itertools::Itertools;
    30
    31
    /// Constant that is used as a placeholder for any valid timezone.
    32
    /// This is used where a function can accept a timestamp type with any
    33
    /// valid timezone, it exists to avoid the need to enumerate all possible
    34
    /// timezones. See [`TypeSignature`] for more details.
    35
    ///
    36
    /// Type coercion always ensures that functions will be executed using
    37
    /// timestamp arrays that have a valid time zone. Functions must never
    38
    /// return results with this timezone.
    39
    pub const TIMEZONE_WILDCARD: &str = "+TZ";
    40
    41
    /// Constant that is used as a placeholder for any valid fixed size list.
    42
    /// This is used where a function can accept a fixed size list type with any
    43
    /// valid length. It exists to avoid the need to enumerate all possible fixed size list lengths.
    44
    pub const FIXED_SIZE_LIST_WILDCARD: i32 = i32::MIN;
    45
    46
    /// How a function's output changes with respect to a fixed input
    47
    ///
    48
    /// The volatility of a function determines eligibility for certain
    49
    /// optimizations. You should always define your function to have the strictest
    50
    /// possible volatility to maximize performance and avoid unexpected
    51
    /// results.
    52
    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
    53
    pub enum Volatility {
    54
        /// Always returns the same output when given the same input.
    55
        ///
    56
        /// DataFusion will inline immutable functions during planning.
    57
        ///
    58
        /// For example, the `abs` function is immutable, so `abs(-1)` will be
    59
        /// evaluated and replaced  with `1` during planning rather than invoking
    60
        /// the function at runtime.
    61
        Immutable,
    62
        /// May return different values given the same input across different
    63
        /// queries but must return the same value for a given input within a query.
    64
        ///
    65
        /// For example, the `now()` function is stable, because the query `select
    66
        /// col1, now() from t1`, will return different results each time it is run,
    67
        /// but within the same query, the output of the `now()` function has the
    68
        /// same value for each output row.
    69
        ///
    70
        /// DataFusion will inline `Stable` functions when possible. For example,
    71
        /// `Stable` functions are inlined when planning a query for execution, but
    72
        /// not in View definitions or prepared statements.
    73
        Stable,
    74
        /// May change the return value from evaluation to evaluation.
    75
        ///
    76
        /// Multiple invocations of a volatile function may return different results
    77
        /// when used in the same query on different rows. An example of this is the
    78
        /// `random()` function.
    79
        ///
    80
        /// DataFusion can not evaluate such functions during planning or push these
    81
        /// predicates into scans. In the query `select col1, random() from t1`,
    82
        /// `random()` function will be evaluated for each output row, resulting in
    83
        /// a unique random value for each row.
    84
        Volatile,
    85
    }
    86
    87
    /// Represents the arity (number of arguments) of a function signature
    88
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    89
    pub enum Arity {
    90
        /// Fixed number of arguments
    91
        Fixed(usize),
    92
        /// Variable number of arguments (e.g., Variadic, VariadicAny, UserDefined)
    93
        Variable,
    94
    }
    95
    96
    /// The types of arguments for which a function has implementations.
    97
    ///
    98
    /// [`TypeSignature`] **DOES NOT** define the types that a user query could call the
    99
    /// function with. DataFusion will automatically coerce (cast) argument types to
    100
    /// one of the supported function signatures, if possible.
    101
    ///
    102
    /// # Overview
    103
    /// Functions typically provide implementations for a small number of different
    104
    /// argument [`DataType`]s, rather than all possible combinations. If a user
    105
    /// calls a function with arguments that do not match any of the declared types,
    106
    /// DataFusion will attempt to automatically coerce (add casts to) function
    107
    /// arguments so they match the [`TypeSignature`]. See the [`type_coercion`] module
    108
    /// for more details
    109
    ///
    110
    /// # Example: Numeric Functions
    111
    /// For example, a function like `cos` may only provide an implementation for
    112
    /// [`DataType::Float64`]. When users call `cos` with a different argument type,
    113
    /// such as `cos(int_column)`, and type coercion automatically adds a cast such
    114
    /// as `cos(CAST int_column AS DOUBLE)` during planning.
    115
    ///
    116
    /// [`type_coercion`]: crate::type_coercion
    117
    ///
    118
    /// ## Example: Strings
    119
    ///
    120
    /// There are several different string types in Arrow, such as
    121
    /// [`DataType::Utf8`], [`DataType::LargeUtf8`], and [`DataType::Utf8View`].
    122
    ///
    123
    /// Some functions may have specialized implementations for these types, while others
    124
    /// may be able to handle only one of them. For example, a function that
    125
    /// only works with [`DataType::Utf8View`] would have the following signature:
    126
    ///
    127
    /// ```
    128
    /// # use arrow::datatypes::DataType;
    129
    /// # use datafusion_expr_common::signature::{TypeSignature};
    130
    /// // Declares the function must be invoked with a single argument of type `Utf8View`.
    131
    /// // if a user calls the function with `Utf8` or `LargeUtf8`, DataFusion will
    132
    /// // automatically add a cast to `Utf8View` during planning.
    133
    /// let type_signature = TypeSignature::Exact(vec![DataType::Utf8View]);
    134
    /// ```
    135
    ///
    136
    /// # Example: Timestamps
    137
    ///
    138
    /// Types to match are represented using Arrow's [`DataType`].  [`DataType::Timestamp`] has an optional variable
    139
    /// timezone specification. To specify a function can handle a timestamp with *ANY* timezone, use
    140
    /// the [`TIMEZONE_WILDCARD`]. For example:
    141
    ///
    142
    /// ```
    143
    /// # use arrow::datatypes::{DataType, TimeUnit};
    144
    /// # use datafusion_expr_common::signature::{TIMEZONE_WILDCARD, TypeSignature};
    145
    /// let type_signature = TypeSignature::Exact(vec![
    146
    ///     // A nanosecond precision timestamp with ANY timezone
    147
    ///     // matches  Timestamp(Nanosecond, Some("+0:00"))
    148
    ///     // matches  Timestamp(Nanosecond, Some("+5:00"))
    149
    ///     // does not match  Timestamp(Nanosecond, None)
    150
    ///     DataType::Timestamp(TimeUnit::Nanosecond, Some(TIMEZONE_WILDCARD.into())),
    151
    /// ]);
    152
    /// ```
    153
    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
    154
    pub enum TypeSignature {
    155
        /// One or more arguments of a common type out of a list of valid types.
    156
        ///
    157
        /// For functions that take no arguments (e.g. `random()` see [`TypeSignature::Nullary`]).
    158
        ///
    159
        /// # Examples
    160
        ///
    161
        /// A function such as `concat` is `Variadic(vec![DataType::Utf8,
    162
        /// DataType::LargeUtf8])`
    163
        Variadic(Vec<DataType>),
    164
        /// The acceptable signature and coercions rules are special for this
    165
        /// function.
    166
        ///
    167
        /// If this signature is specified,
    168
        /// DataFusion will call [`ScalarUDFImpl::coerce_types`] to prepare argument types.
    169
        ///
    170
        /// [`ScalarUDFImpl::coerce_types`]: https://docs.rs/datafusion/latest/datafusion/logical_expr/trait.ScalarUDFImpl.html#method.coerce_types
    171
        UserDefined,
    172
        /// One or more arguments with arbitrary types
    173
        VariadicAny,
    174
        /// One or more arguments of an arbitrary but equal type out of a list of valid types.
    175
        ///
    176
        /// # Examples
    177
        ///
    178
        /// 1. A function of one argument of f64 is `Uniform(1, vec![DataType::Float64])`
    179
        /// 2. A function of one argument of f64 or f32 is `Uniform(1, vec![DataType::Float32, DataType::Float64])`
    180
        Uniform(usize, Vec<DataType>),
    181
        /// One or more arguments with exactly the specified types in order.
    182
        ///
    183
        /// For functions that take no arguments (e.g. `random()`) use [`TypeSignature::Nullary`].
    184
        Exact(Vec<DataType>),
    185
        /// One or more arguments belonging to the [`TypeSignatureClass`], in order.
    186
        ///
    187
        /// [`Coercion`] contains not only the desired type but also the allowed
    188
        /// casts. For example, if you expect a function has string type, but you
    189
        /// also allow it to be casted from binary type.
    190
        ///
    191
        /// For functions that take no arguments (e.g. `random()`) see [`TypeSignature::Nullary`].
    192
        Coercible(Vec<Coercion>),
    193
        /// One or more arguments coercible to a single, comparable type.
    194
        ///
    195
        /// Each argument will be coerced to a single type using the
    196
        /// coercion rules described in [`comparison_coercion_numeric`].
    197
        ///
    198
        /// # Examples
    199
        ///
    200
        /// If the `nullif(1, 2)` function is called with `i32` and `i64` arguments
    201
        /// the types will both be coerced to `i64` before the function is invoked.
    202
        ///
    203
        /// If the `nullif('1', 2)` function is called with `Utf8` and `i64` arguments
    204
        /// the types will both be coerced to `Utf8` before the function is invoked.
    205
        ///
    206
        /// Note:
    207
        /// - For functions that take no arguments (e.g. `random()` see [`TypeSignature::Nullary`]).
    208
        /// - If all arguments have type [`DataType::Null`], they are coerced to `Utf8`
    209
        ///
    210
        /// [`comparison_coercion_numeric`]: crate::type_coercion::binary::comparison_coercion_numeric
    211
        Comparable(usize),
    212
        /// One or more arguments of arbitrary types.
    213
        ///
    214
        /// For functions that take no arguments (e.g. `random()`) use [`TypeSignature::Nullary`].
    215
        Any(usize),
    216
        /// Matches exactly one of a list of [`TypeSignature`]s.
    217
        ///
    218
        /// Coercion is attempted to match the signatures in order, and stops after
    219
        /// the first success, if any.
    220
        ///
    221
        /// # Examples
    222
        ///
    223
        /// Since `make_array` takes 0 or more arguments with arbitrary types, its `TypeSignature`
    224
        /// is `OneOf(vec![Any(0), VariadicAny])`.
    225
        OneOf(Vec<TypeSignature>),
    226
        /// A function that has an [`ArrayFunctionSignature`]
    227
        ArraySignature(ArrayFunctionSignature),
    228
        /// One or more arguments of numeric types, coerced to a common numeric type.
    229
        ///
    230
        /// See [`NativeType::is_numeric`] to know which type is considered numeric
    231
        ///
    232
        /// For functions that take no arguments (e.g. `random()`) use [`TypeSignature::Nullary`].
    233
        ///
    234
        /// [`NativeType::is_numeric`]: datafusion_common::types::NativeType::is_numeric
    235
        Numeric(usize),
    236
        /// One or arguments of all the same string types.
    237
        ///
    238
        /// The precedence of type from high to low is Utf8View, LargeUtf8 and Utf8.
    239
        /// Null is considered as `Utf8` by default
    240
        /// Dictionary with string value type is also handled.
    241
        ///
    242
        /// For example, if a function is called with (utf8, large_utf8), all
    243
        /// arguments will be coerced to  `LargeUtf8`
    244
        ///
    245
        /// For functions that take no arguments (e.g. `random()` use [`TypeSignature::Nullary`]).
    246
        String(usize),
    247
        /// No arguments
    248
        Nullary,
    249
    }
    250
    251
    impl TypeSignature {
    252
        #[inline]
    253
    0
        pub fn is_one_of(&self) -> bool {
    254
    0
            matches!(self, TypeSignature::OneOf(_))
    255
    0
        }
    256
    257
        /// Returns the arity (expected number of arguments) for this type signature.
    258
        ///
    259
        /// Returns `Arity::Fixed(n)` for signatures with a specific argument count,
    260
        /// or `Arity::Variable` for variable-arity signatures like `Variadic`, `VariadicAny`, `UserDefined`.
    261
        ///
    262
        /// # Examples
    263
        ///
    264
        /// ```
    265
        /// # use datafusion_expr_common::signature::{TypeSignature, Arity};
    266
        /// # use arrow::datatypes::DataType;
    267
        /// // Exact signature has fixed arity
    268
        /// let sig = TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8]);
    269
        /// assert_eq!(sig.arity(), Arity::Fixed(2));
    270
        ///
    271
        /// // Variadic signature has variable arity
    272
        /// let sig = TypeSignature::VariadicAny;
    273
        /// assert_eq!(sig.arity(), Arity::Variable);
    274
        /// ```
    275
    0
        pub fn arity(&self) -> Arity {
    276
    0
            match self {
    277
    0
                TypeSignature::Exact(types) => Arity::Fixed(types.len()),
    278
    0
                TypeSignature::Uniform(count, _) => Arity::Fixed(*count),
    279
    0
                TypeSignature::Numeric(count) => Arity::Fixed(*count),
    280
    0
                TypeSignature::String(count) => Arity::Fixed(*count),
    281
    0
                TypeSignature::Comparable(count) => Arity::Fixed(*count),
    282
    0
                TypeSignature::Any(count) => Arity::Fixed(*count),
    283
    0
                TypeSignature::Coercible(types) => Arity::Fixed(types.len()),
    284
    0
                TypeSignature::Nullary => Arity::Fixed(0),
    285
                TypeSignature::ArraySignature(ArrayFunctionSignature::Array {
    286
    0
                    arguments,
    287
                    ..
    288
    0
                }) => Arity::Fixed(arguments.len()),
    289
                TypeSignature::ArraySignature(ArrayFunctionSignature::RecursiveArray) => {
    290
    0
                    Arity::Fixed(1)
    291
                }
    292
                TypeSignature::ArraySignature(ArrayFunctionSignature::MapArray) => {
    293
    0
                    Arity::Fixed(1)
    294
                }
    295
    0
                TypeSignature::OneOf(variants) => {
    296
                    // If any variant is Variable, the whole OneOf is Variable
    297
    0
                    let has_variable = variants.iter().any(|v| v.arity() == Arity::Variable);
    298
    0
                    if has_variable {
    299
    0
                        return Arity::Variable;
    300
    0
                    }
    301
                    // Otherwise, get max arity from all fixed arity variants
    302
    0
                    let max_arity = variants
    303
    0
                        .iter()
    304
    0
                        .filter_map(|v| match v.arity() {
    305
    0
                            Arity::Fixed(n) => Some(n),
    306
    0
                            Arity::Variable => None,
    307
    0
                        })
    308
    0
                        .max();
    309
    0
                    match max_arity {
    310
    0
                        Some(n) => Arity::Fixed(n),
    311
    0
                        None => Arity::Variable,
    312
                    }
    313
                }
    314
                TypeSignature::Variadic(_)
    315
                | TypeSignature::VariadicAny
    316
    0
                | TypeSignature::UserDefined => Arity::Variable,
    317
            }
    318
    0
        }
    319
    }
    320
    321
    /// Represents the class of types that can be used in a function signature.
    322
    ///
    323
    /// This is used to specify what types are valid for function arguments in a more flexible way than
    324
    /// just listing specific DataTypes. For example, TypeSignatureClass::Timestamp matches any timestamp
    325
    /// type regardless of timezone or precision.
    326
    ///
    327
    /// Used primarily with [`TypeSignature::Coercible`] to define function signatures that can accept
    328
    /// arguments that can be coerced to a particular class of types.
    329
    #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Hash)]
    330
    pub enum TypeSignatureClass {
    331
        Timestamp,
    332
        Time,
    333
        Interval,
    334
        Duration,
    335
        Native(LogicalTypeRef),
    336
        Integer,
    337
        Float,
    338
        Decimal,
    339
        Numeric,
    340
        /// Encompasses both the native Binary as well as arbitrarily sized FixedSizeBinary types
    341
        Binary,
    342
    }
    343
    344
    impl Display for TypeSignatureClass {
    345
    0
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    346
    0
            write!(f, "TypeSignatureClass::{self:?}")
    347
    0
        }
    348
    }
    349
    350
    impl TypeSignatureClass {
    351
        /// Get example acceptable types for this `TypeSignatureClass`
    352
        ///
    353
        /// This is used for `information_schema` and can be used to generate
    354
        /// documentation or error messages.
    355
    0
        fn get_example_types(&self) -> Vec<DataType> {
    356
    0
            match self {
    357
    0
                TypeSignatureClass::Native(l) => get_data_types(l.native()),
    358
                TypeSignatureClass::Timestamp => {
    359
    0
                    vec![
    360
    0
                        DataType::Timestamp(TimeUnit::Nanosecond, None),
    361
    0
                        DataType::Timestamp(
    362
    0
                            TimeUnit::Nanosecond,
    363
    0
                            Some(TIMEZONE_WILDCARD.into()),
    364
    0
                        ),
    365
                    ]
    366
                }
    367
                TypeSignatureClass::Time => {
    368
    0
                    vec![DataType::Time64(TimeUnit::Nanosecond)]
    369
                }
    370
                TypeSignatureClass::Interval => {
    371
    0
                    vec![DataType::Interval(IntervalUnit::DayTime)]
    372
                }
    373
                TypeSignatureClass::Duration => {
    374
    0
                    vec![DataType::Duration(TimeUnit::Nanosecond)]
    375
                }
    376
                TypeSignatureClass::Integer => {
    377
    0
                    vec![DataType::Int64]
    378
                }
    379
                TypeSignatureClass::Binary => {
    380
    0
                    vec![DataType::Binary]
    381
                }
    382
    0
                TypeSignatureClass::Decimal => vec![Decimal128Type::DEFAULT_TYPE],
    383
    0
                TypeSignatureClass::Float => vec![DataType::Float64],
    384
    0
                TypeSignatureClass::Numeric => vec![
    385
    0
                    DataType::Float64,
    386
    0
                    DataType::Int64,
    387
    0
                    Decimal128Type::DEFAULT_TYPE,
    388
                ],
    389
            }
    390
    0
        }
    391
    392
        /// Does the specified `NativeType` match this type signature class?
    393
    0
        pub fn matches_native_type(&self, logical_type: &NativeType) -> bool {
    394
    0
            if logical_type == &NativeType::Null {
    395
    0
                return true;
    396
    0
            }
    397
    398
    0
            match self {
    399
    0
                TypeSignatureClass::Native(t) if t.native() == logical_type => true,
    400
    0
                TypeSignatureClass::Timestamp if logical_type.is_timestamp() => true,
    401
    0
                TypeSignatureClass::Time if logical_type.is_time() => true,
    402
    0
                TypeSignatureClass::Interval if logical_type.is_interval() => true,
    403
    0
                TypeSignatureClass::Duration if logical_type.is_duration() => true,
    404
    0
                TypeSignatureClass::Integer if logical_type.is_integer() => true,
    405
    0
                TypeSignatureClass::Binary if logical_type.is_binary() => true,
    406
    0
                TypeSignatureClass::Decimal if logical_type.is_decimal() => true,
    407
    0
                TypeSignatureClass::Float if logical_type.is_float() => true,
    408
    0
                TypeSignatureClass::Numeric if logical_type.is_numeric() => true,
    409
    0
                _ => false,
    410
            }
    411
    0
        }
    412
    413
        /// What type would `origin_type` be casted to when casting to the specified native type?
    414
    0
        pub fn default_casted_type(
    415
    0
            &self,
    416
    0
            native_type: &NativeType,
    417
    0
            origin_type: &DataType,
    418
    0
        ) -> Result<DataType> {
    419
    0
            match self {
    420
    0
                TypeSignatureClass::Native(logical_type) => {
    421
    0
                    logical_type.native().default_cast_for(origin_type)
    422
                }
    423
                // If the given type is already a timestamp, we don't change the unit and timezone
    424
    0
                TypeSignatureClass::Timestamp if native_type.is_timestamp() => {
    425
    0
                    Ok(origin_type.to_owned())
    426
                }
    427
    0
                TypeSignatureClass::Time if native_type.is_time() => {
    428
    0
                    Ok(origin_type.to_owned())
    429
                }
    430
    0
                TypeSignatureClass::Interval if native_type.is_interval() => {
    431
    0
                    Ok(origin_type.to_owned())
    432
                }
    433
    0
                TypeSignatureClass::Duration if native_type.is_duration() => {
    434
    0
                    Ok(origin_type.to_owned())
    435
                }
    436
    0
                TypeSignatureClass::Integer if native_type.is_integer() => {
    437
    0
                    Ok(origin_type.to_owned())
    438
                }
    439
    0
                TypeSignatureClass::Binary if native_type.is_binary() => {
    440
    0
                    Ok(origin_type.to_owned())
    441
                }
    442
    0
                TypeSignatureClass::Decimal if native_type.is_decimal() => {
    443
    0
                    Ok(origin_type.to_owned())
    444
                }
    445
    0
                TypeSignatureClass::Float if native_type.is_float() => {
    446
    0
                    Ok(origin_type.to_owned())
    447
                }
    448
    0
                TypeSignatureClass::Numeric if native_type.is_numeric() => {
    449
    0
                    Ok(origin_type.to_owned())
    450
                }
    451
    0
                _ if native_type.is_null() => Ok(origin_type.to_owned()),
    452
    0
                _ => internal_err!("May miss the matching logic in `matches_native_type`"),
    453
            }
    454
    0
        }
    455
    }
    456
    457
    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
    458
    pub enum ArrayFunctionSignature {
    459
        /// A function takes at least one List/LargeList/FixedSizeList argument.
    460
        Array {
    461
            /// A full list of the arguments accepted by this function.
    462
            arguments: Vec<ArrayFunctionArgument>,
    463
            /// Additional information about how array arguments should be coerced.
    464
            array_coercion: Option<ListCoercion>,
    465
        },
    466
        /// A function takes a single argument that must be a List/LargeList/FixedSizeList
    467
        /// which gets coerced to List, with element type recursively coerced to List too if it is list-like.
    468
        RecursiveArray,
    469
        /// Specialized Signature for MapArray
    470
        /// The function takes a single argument that must be a MapArray
    471
        MapArray,
    472
    }
    473
    474
    impl Display for ArrayFunctionSignature {
    475
    0
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    476
    0
            match self {
    477
    0
                ArrayFunctionSignature::Array { arguments, .. } => {
    478
    0
                    for (idx, argument) in arguments.iter().enumerate() {
    479
    0
                        write!(f, "{argument}")?;
    480
    0
                        if idx != arguments.len() - 1 {
    481
    0
                            write!(f, ", ")?;
    482
    0
                        }
    483
                    }
    484
    0
                    Ok(())
    485
                }
    486
                ArrayFunctionSignature::RecursiveArray => {
    487
    0
                    write!(f, "recursive_array")
    488
                }
    489
                ArrayFunctionSignature::MapArray => {
    490
    0
                    write!(f, "map_array")
    491
                }
    492
            }
    493
    0
        }
    494
    }
    495
    496
    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
    497
    pub enum ArrayFunctionArgument {
    498
        /// A non-list or list argument. The list dimensions should be one less than the Array's list
    499
        /// dimensions.
    500
        Element,
    501
        /// An Int64 index argument.
    502
        Index,
    503
        /// An argument of type List/LargeList/FixedSizeList. All Array arguments must be coercible
    504
        /// to the same type.
    505
        Array,
    506
        // A Utf8 argument.
    507
        String,
    508
    }
    509
    510
    impl Display for ArrayFunctionArgument {
    511
    0
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    512
    0
            match self {
    513
                ArrayFunctionArgument::Element => {
    514
    0
                    write!(f, "element")
    515
                }
    516
                ArrayFunctionArgument::Index => {
    517
    0
                    write!(f, "index")
    518
                }
    519
                ArrayFunctionArgument::Array => {
    520
    0
                    write!(f, "array")
    521
                }
    522
                ArrayFunctionArgument::String => {
    523
    0
                    write!(f, "string")
    524
                }
    525
            }
    526
    0
        }
    527
    }
    528
    529
    impl TypeSignature {
    530
    0
        pub fn to_string_repr(&self) -> Vec<String> {
    531
    0
            match self {
    532
                TypeSignature::Nullary => {
    533
    0
                    vec!["NullAry()".to_string()]
    534
                }
    535
    0
                TypeSignature::Variadic(types) => {
    536
    0
                    vec![format!("{}, ..", Self::join_types(types, "/"))]
    537
                }
    538
    0
                TypeSignature::Uniform(arg_count, valid_types) => {
    539
    0
                    vec![
    540
    0
                        std::iter::repeat_n(Self::join_types(valid_types, "/"), *arg_count)
    541
    0
                            .collect::<Vec<String>>()
    542
    0
                            .join(", "),
    543
                    ]
    544
                }
    545
    0
                TypeSignature::String(num) => {
    546
    0
                    vec![format!("String({num})")]
    547
                }
    548
    0
                TypeSignature::Numeric(num) => {
    549
    0
                    vec![format!("Numeric({num})")]
    550
                }
    551
    0
                TypeSignature::Comparable(num) => {
    552
    0
                    vec![format!("Comparable({num})")]
    553
                }
    554
    0
                TypeSignature::Coercible(coercions) => {
    555
    0
                    vec![Self::join_types(coercions, ", ")]
    556
                }
    557
    0
                TypeSignature::Exact(types) => {
    558
    0
                    vec![Self::join_types(types, ", ")]
    559
                }
    560
    0
                TypeSignature::Any(arg_count) => {
    561
    0
                    vec![std::iter::repeat_n("Any", *arg_count)
    562
    0
                        .collect::<Vec<&str>>()
    563
    0
                        .join(", ")]
    564
                }
    565
                TypeSignature::UserDefined => {
    566
    0
                    vec!["UserDefined".to_string()]
    567
                }
    568
    0
                TypeSignature::VariadicAny => vec!["Any, .., Any".to_string()],
    569
    0
                TypeSignature::OneOf(sigs) => {
    570
    0
                    sigs.iter().flat_map(|s| s.to_string_repr()).collect()
    571
                }
    572
    0
                TypeSignature::ArraySignature(array_signature) => {
    573
    0
                    vec![array_signature.to_string()]
    574
                }
    575
            }
    576
    0
        }
    577
    578
        /// Return string representation of the function signature with parameter names.
    579
        ///
    580
        /// This method is similar to [`Self::to_string_repr`] but uses parameter names
    581
        /// instead of types when available. This is useful for generating more helpful
    582
        /// error messages.
    583
        ///
    584
        /// # Arguments
    585
        /// * `parameter_names` - Optional slice of parameter names. When provided, these
    586
        ///   names will be used instead of type names in the output.
    587
        ///
    588
        /// # Examples
    589
        /// ```
    590
        /// # use datafusion_expr_common::signature::TypeSignature;
    591
        /// # use arrow::datatypes::DataType;
    592
        /// let sig = TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8]);
    593
        ///
    594
        /// // Without names: shows types only
    595
        /// assert_eq!(sig.to_string_repr_with_names(None), vec!["Int32, Utf8"]);
    596
        ///
    597
        /// // With names: shows parameter names with types
    598
        /// assert_eq!(
    599
        ///     sig.to_string_repr_with_names(Some(&["id".to_string(), "name".to_string()])),
    600
        ///     vec!["id: Int32, name: Utf8"]
    601
        /// );
    602
        /// ```
    603
    0
        pub fn to_string_repr_with_names(
    604
    0
            &self,
    605
    0
            parameter_names: Option<&[String]>,
    606
    0
        ) -> Vec<String> {
    607
    0
            match self {
    608
    0
                TypeSignature::Exact(types) => {
    609
    0
                    if let Some(names) = parameter_names {
    610
    0
                        vec![names
    611
    0
                            .iter()
    612
    0
                            .zip(types.iter())
    613
    0
                            .map(|(name, typ)| format!("{name}: {typ}"))
    614
    0
                            .collect::<Vec<_>>()
    615
    0
                            .join(", ")]
    616
                    } else {
    617
    0
                        vec![Self::join_types(types, ", ")]
    618
                    }
    619
                }
    620
    0
                TypeSignature::Any(count) => {
    621
    0
                    if let Some(names) = parameter_names {
    622
    0
                        vec![names
    623
    0
                            .iter()
    624
    0
                            .take(*count)
    625
    0
                            .map(|name| format!("{name}: Any"))
    626
    0
                            .collect::<Vec<_>>()
    627
    0
                            .join(", ")]
    628
                    } else {
    629
    0
                        vec![std::iter::repeat_n("Any", *count)
    630
    0
                            .collect::<Vec<&str>>()
    631
    0
                            .join(", ")]
    632
                    }
    633
                }
    634
    0
                TypeSignature::Uniform(count, types) => {
    635
    0
                    if let Some(names) = parameter_names {
    636
    0
                        let type_str = Self::join_types(types, "/");
    637
    0
                        vec![names
    638
    0
                            .iter()
    639
    0
                            .take(*count)
    640
    0
                            .map(|name| format!("{name}: {type_str}"))
    641
    0
                            .collect::<Vec<_>>()
    642
    0
                            .join(", ")]
    643
                    } else {
    644
    0
                        self.to_string_repr()
    645
                    }
    646
                }
    647
    0
                TypeSignature::Coercible(coercions) => {
    648
    0
                    if let Some(names) = parameter_names {
    649
    0
                        vec![names
    650
    0
                            .iter()
    651
    0
                            .zip(coercions.iter())
    652
    0
                            .map(|(name, coercion)| format!("{name}: {coercion}"))
    653
    0
                            .collect::<Vec<_>>()
    654
    0
                            .join(", ")]
    655
                    } else {
    656
    0
                        vec![Self::join_types(coercions, ", ")]
    657
                    }
    658
                }
    659
    0
                TypeSignature::Comparable(count) => {
    660
    0
                    if let Some(names) = parameter_names {
    661
    0
                        vec![names
    662
    0
                            .iter()
    663
    0
                            .take(*count)
    664
    0
                            .map(|name| format!("{name}: Comparable"))
    665
    0
                            .collect::<Vec<_>>()
    666
    0
                            .join(", ")]
    667
                    } else {
    668
    0
                        self.to_string_repr()
    669
                    }
    670
                }
    671
    0
                TypeSignature::Numeric(count) => {
    672
    0
                    if let Some(names) = parameter_names {
    673
    0
                        vec![names
    674
    0
                            .iter()
    675
    0
                            .take(*count)
    676
    0
                            .map(|name| format!("{name}: Numeric"))
    677
    0
                            .collect::<Vec<_>>()
    678
    0
                            .join(", ")]
    679
                    } else {
    680
    0
                        self.to_string_repr()
    681
                    }
    682
                }
    683
    0
                TypeSignature::String(count) => {
    684
    0
                    if let Some(names) = parameter_names {
    685
    0
                        vec![names
    686
    0
                            .iter()
    687
    0
                            .take(*count)
    688
    0
                            .map(|name| format!("{name}: String"))
    689
    0
                            .collect::<Vec<_>>()
    690
    0
                            .join(", ")]
    691
                    } else {
    692
    0
                        self.to_string_repr()
    693
                    }
    694
                }
    695
    0
                TypeSignature::Nullary => self.to_string_repr(),
    696
    0
                TypeSignature::ArraySignature(array_sig) => {
    697
    0
                    if let Some(names) = parameter_names {
    698
    0
                        match array_sig {
    699
    0
                            ArrayFunctionSignature::Array { arguments, .. } => {
    700
    0
                                vec![names
    701
    0
                                    .iter()
    702
    0
                                    .zip(arguments.iter())
    703
    0
                                    .map(|(name, arg_type)| format!("{name}: {arg_type}"))
    704
    0
                                    .collect::<Vec<_>>()
    705
    0
                                    .join(", ")]
    706
                            }
    707
                            ArrayFunctionSignature::RecursiveArray => {
    708
    0
                                vec![names
    709
    0
                                    .iter()
    710
    0
                                    .take(1)
    711
    0
                                    .map(|name| format!("{name}: recursive_array"))
    712
    0
                                    .collect::<Vec<_>>()
    713
    0
                                    .join(", ")]
    714
                            }
    715
                            ArrayFunctionSignature::MapArray => {
    716
    0
                                vec![names
    717
    0
                                    .iter()
    718
    0
                                    .take(1)
    719
    0
                                    .map(|name| format!("{name}: map_array"))
    720
    0
                                    .collect::<Vec<_>>()
    721
    0
                                    .join(", ")]
    722
                            }
    723
                        }
    724
                    } else {
    725
    0
                        self.to_string_repr()
    726
                    }
    727
                }
    728
    0
                TypeSignature::OneOf(sigs) => sigs
    729
    0
                    .iter()
    730
    0
                    .flat_map(|s| s.to_string_repr_with_names(parameter_names))
    731
    0
                    .collect(),
    732
                TypeSignature::UserDefined => {
    733
    0
                    if let Some(names) = parameter_names {
    734
    0
                        vec![names.join(", ")]
    735
                    } else {
    736
    0
                        self.to_string_repr()
    737
                    }
    738
                }
    739
                // Variable arity signatures cannot use parameter names
    740
                TypeSignature::Variadic(_) | TypeSignature::VariadicAny => {
    741
    0
                    self.to_string_repr()
    742
                }
    743
            }
    744
    0
        }
    745
    746
        /// Helper function to join types with specified delimiter.
    747
    0
        pub fn join_types<T: Display>(types: &[T], delimiter: &str) -> String {
    748
    0
            types
    749
    0
                .iter()
    750
    0
                .map(|t| t.to_string())
    751
    0
                .collect::<Vec<String>>()
    752
    0
                .join(delimiter)
    753
    0
        }
    754
    755
        /// Check whether 0 input argument is valid for given `TypeSignature`
    756
    0
        pub fn supports_zero_argument(&self) -> bool {
    757
    0
            match &self {
    758
    0
                TypeSignature::Exact(vec) => vec.is_empty(),
    759
    0
                TypeSignature::Nullary => true,
    760
    0
                TypeSignature::OneOf(types) => types
    761
    0
                    .iter()
    762
    0
                    .any(|type_sig| type_sig.supports_zero_argument()),
    763
    0
                _ => false,
    764
            }
    765
    0
        }
    766
    767
        /// Returns true if the signature currently supports or used to supported 0
    768
        /// input arguments in a previous version of DataFusion.
    769
    0
        pub fn used_to_support_zero_arguments(&self) -> bool {
    770
    0
            match &self {
    771
    0
                TypeSignature::Any(num) => *num == 0,
    772
    0
                _ => self.supports_zero_argument(),
    773
            }
    774
    0
        }
    775
    776
        #[deprecated(since = "46.0.0", note = "See get_example_types instead")]
    777
    0
        pub fn get_possible_types(&self) -> Vec<Vec<DataType>> {
    778
    0
            self.get_example_types()
    779
    0
        }
    780
    781
        /// Return example acceptable types for this `TypeSignature`'
    782
        ///
    783
        /// Returns a `Vec<DataType>` for each argument to the function
    784
        ///
    785
        /// This is used for `information_schema` and can be used to generate
    786
        /// documentation or error messages.
    787
    0
        pub fn get_example_types(&self) -> Vec<Vec<DataType>> {
    788
    0
            match self {
    789
    0
                TypeSignature::Exact(types) => vec![types.clone()],
    790
    0
                TypeSignature::OneOf(types) => types
    791
    0
                    .iter()
    792
    0
                    .flat_map(|type_sig| type_sig.get_example_types())
    793
    0
                    .collect(),
    794
    0
                TypeSignature::Uniform(arg_count, types) => types
    795
    0
                    .iter()
    796
    0
                    .cloned()
    797
    0
                    .map(|data_type| vec![data_type; *arg_count])
    798
    0
                    .collect(),
    799
    0
                TypeSignature::Coercible(coercions) => coercions
    800
    0
                    .iter()
    801
    0
                    .map(|c| {
    802
    0
                        let mut all_types: IndexSet<DataType> =
    803
    0
                            c.desired_type().get_example_types().into_iter().collect();
    804
    805
    0
                        if let Some(implicit_coercion) = c.implicit_coercion() {
    806
    0
                            let allowed_casts: Vec<DataType> = implicit_coercion
    807
    0
                                .allowed_source_types
    808
    0
                                .iter()
    809
    0
                                .flat_map(|t| t.get_example_types())
    810
    0
                                .collect();
    811
    0
                            all_types.extend(allowed_casts);
    812
    0
                        }
    813
    814
    0
                        all_types.into_iter().collect::<Vec<_>>()
    815
    0
                    })
    816
    0
                    .multi_cartesian_product()
    817
    0
                    .collect(),
    818
    0
                TypeSignature::Variadic(types) => types
    819
    0
                    .iter()
    820
    0
                    .cloned()
    821
    0
                    .map(|data_type| vec![data_type])
    822
    0
                    .collect(),
    823
    0
                TypeSignature::Numeric(arg_count) => NUMERICS
    824
    0
                    .iter()
    825
    0
                    .cloned()
    826
    0
                    .map(|numeric_type| vec![numeric_type; *arg_count])
    827
    0
                    .collect(),
    828
    0
                TypeSignature::String(arg_count) => get_data_types(&NativeType::String)
    829
    0
                    .into_iter()
    830
    0
                    .map(|dt| vec![dt; *arg_count])
    831
    0
                    .collect::<Vec<_>>(),
    832
                // TODO: Implement for other types
    833
                TypeSignature::Any(_)
    834
                | TypeSignature::Comparable(_)
    835
                | TypeSignature::Nullary
    836
                | TypeSignature::VariadicAny
    837
                | TypeSignature::ArraySignature(_)
    838
    0
                | TypeSignature::UserDefined => vec![],
    839
            }
    840
    0
        }
    841
    }
    842
    843
    0
    fn get_data_types(native_type: &NativeType) -> Vec<DataType> {
    844
    0
        match native_type {
    845
    0
            NativeType::Null => vec![DataType::Null],
    846
    0
            NativeType::Boolean => vec![DataType::Boolean],
    847
    0
            NativeType::Int8 => vec![DataType::Int8],
    848
    0
            NativeType::Int16 => vec![DataType::Int16],
    849
    0
            NativeType::Int32 => vec![DataType::Int32],
    850
    0
            NativeType::Int64 => vec![DataType::Int64],
    851
    0
            NativeType::UInt8 => vec![DataType::UInt8],
    852
    0
            NativeType::UInt16 => vec![DataType::UInt16],
    853
    0
            NativeType::UInt32 => vec![DataType::UInt32],
    854
    0
            NativeType::UInt64 => vec![DataType::UInt64],
    855
    0
            NativeType::Float16 => vec![DataType::Float16],
    856
    0
            NativeType::Float32 => vec![DataType::Float32],
    857
    0
            NativeType::Float64 => vec![DataType::Float64],
    858
    0
            NativeType::Date => vec![DataType::Date32, DataType::Date64],
    859
    0
            NativeType::Binary => vec![
    860
    0
                DataType::Binary,
    861
    0
                DataType::LargeBinary,
    862
    0
                DataType::BinaryView,
    863
            ],
    864
            NativeType::String => {
    865
    0
                vec![DataType::Utf8, DataType::LargeUtf8, DataType::Utf8View]
    866
            }
    867
            // TODO: support other native types
    868
    0
            _ => vec![],
    869
        }
    870
    0
    }
    871
    872
    /// Represents type coercion rules for function arguments, specifying both the desired type
    873
    /// and optional implicit coercion rules for source types.
    874
    ///
    875
    /// # Examples
    876
    ///
    877
    /// ```
    878
    /// use datafusion_common::types::{logical_binary, logical_string, NativeType};
    879
    /// use datafusion_expr_common::signature::{Coercion, TypeSignatureClass};
    880
    ///
    881
    /// // Exact coercion that only accepts timestamp types
    882
    /// let exact = Coercion::new_exact(TypeSignatureClass::Timestamp);
    883
    ///
    884
    /// // Implicit coercion that accepts string types but can coerce from binary types
    885
    /// let implicit = Coercion::new_implicit(
    886
    ///     TypeSignatureClass::Native(logical_string()),
    887
    ///     vec![TypeSignatureClass::Native(logical_binary())],
    888
    ///     NativeType::String,
    889
    /// );
    890
    /// ```
    891
    ///
    892
    /// There are two variants:
    893
    ///
    894
    /// * `Exact` - Only accepts arguments that exactly match the desired type
    895
    /// * `Implicit` - Accepts the desired type and can coerce from specified source types
    896
    #[derive(Debug, Clone, Eq, PartialOrd)]
    897
    pub enum Coercion {
    898
        /// Coercion that only accepts arguments exactly matching the desired type.
    899
        Exact {
    900
            /// The required type for the argument
    901
            desired_type: TypeSignatureClass,
    902
        },
    903
    904
        /// Coercion that accepts the desired type and can implicitly coerce from other types.
    905
        Implicit {
    906
            /// The primary desired type for the argument
    907
            desired_type: TypeSignatureClass,
    908
            /// Rules for implicit coercion from other types
    909
            implicit_coercion: ImplicitCoercion,
    910
        },
    911
    }
    912
    913
    impl Coercion {
    914
    0
        pub fn new_exact(desired_type: TypeSignatureClass) -> Self {
    915
    0
            Self::Exact { desired_type }
    916
    0
        }
    917
    918
        /// Create a new coercion with implicit coercion rules.
    919
        ///
    920
        /// `allowed_source_types` defines the possible types that can be coerced to `desired_type`.
    921
        /// `default_casted_type` is the default type to be used for coercion if we cast from other types via `allowed_source_types`.
    922
    0
        pub fn new_implicit(
    923
    0
            desired_type: TypeSignatureClass,
    924
    0
            allowed_source_types: Vec<TypeSignatureClass>,
    925
    0
            default_casted_type: NativeType,
    926
    0
        ) -> Self {
    927
    0
            Self::Implicit {
    928
    0
                desired_type,
    929
    0
                implicit_coercion: ImplicitCoercion {
    930
    0
                    allowed_source_types,
    931
    0
                    default_casted_type,
    932
    0
                },
    933
    0
            }
    934
    0
        }
    935
    936
    0
        pub fn allowed_source_types(&self) -> &[TypeSignatureClass] {
    937
    0
            match self {
    938
    0
                Coercion::Exact { .. } => &[],
    939
                Coercion::Implicit {
    940
    0
                    implicit_coercion, ..
    941
    0
                } => implicit_coercion.allowed_source_types.as_slice(),
    942
            }
    943
    0
        }
    944
    945
    0
        pub fn default_casted_type(&self) -> Option<&NativeType> {
    946
    0
            match self {
    947
    0
                Coercion::Exact { .. } => None,
    948
                Coercion::Implicit {
    949
    0
                    implicit_coercion, ..
    950
    0
                } => Some(&implicit_coercion.default_casted_type),
    951
            }
    952
    0
        }
    953
    954
    0
        pub fn desired_type(&self) -> &TypeSignatureClass {
    955
    0
            match self {
    956
    0
                Coercion::Exact { desired_type } => desired_type,
    957
    0
                Coercion::Implicit { desired_type, .. } => desired_type,
    958
            }
    959
    0
        }
    960
    961
    0
        pub fn implicit_coercion(&self) -> Option<&ImplicitCoercion> {
    962
    0
            match self {
    963
    0
                Coercion::Exact { .. } => None,
    964
                Coercion::Implicit {
    965
    0
                    implicit_coercion, ..
    966
    0
                } => Some(implicit_coercion),
    967
            }
    968
    0
        }
    969
    }
    970
    971
    impl Display for Coercion {
    972
    0
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    973
    0
            write!(f, "Coercion({}", self.desired_type())?;
    974
    0
            if let Some(implicit_coercion) = self.implicit_coercion() {
    975
    0
                write!(f, ", implicit_coercion={implicit_coercion}",)
    976
            } else {
    977
    0
                write!(f, ")")
    978
            }
    979
    0
        }
    980
    }
    981
    982
    impl PartialEq for Coercion {
    983
    0
        fn eq(&self, other: &Self) -> bool {
    984
    0
            self.desired_type() == other.desired_type()
    985
    0
                && self.implicit_coercion() == other.implicit_coercion()
    986
    0
        }
    987
    }
    988
    989
    impl Hash for Coercion {
    990
    0
        fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    991
    0
            self.desired_type().hash(state);
    992
    0
            self.implicit_coercion().hash(state);
    993
    0
        }
    994
    }
    995
    996
    /// Defines rules for implicit type coercion, specifying which source types can be
    997
    /// coerced and the default type to use when coercing.
    998
    ///
    999
    /// This is used by functions to specify which types they can accept via implicit
    1000
    /// coercion in addition to their primary desired type.
    1001
    ///
    1002
    /// # Examples
    1003
    ///
    1004
    /// ```
    1005
    /// use arrow::datatypes::TimeUnit;
    1006
    ///
    1007
    /// use datafusion_expr_common::signature::{Coercion, ImplicitCoercion, TypeSignatureClass};
    1008
    /// use datafusion_common::types::{NativeType, logical_binary};
    1009
    ///
    1010
    /// // Allow coercing from binary types to timestamp, coerce to specific timestamp unit and timezone
    1011
    /// let implicit = Coercion::new_implicit(
    1012
    ///     TypeSignatureClass::Timestamp,
    1013
    ///     vec![TypeSignatureClass::Native(logical_binary())],
    1014
    ///     NativeType::Timestamp(TimeUnit::Second, None),
    1015
    /// );
    1016
    /// ```
    1017
    #[derive(Debug, Clone, Eq, PartialOrd)]
    1018
    pub struct ImplicitCoercion {
    1019
        /// The types that can be coerced from via implicit casting
    1020
        allowed_source_types: Vec<TypeSignatureClass>,
    1021
    1022
        /// The default type to use when coercing from allowed source types.
    1023
        /// This is particularly important for types like Timestamp that have multiple
    1024
        /// possible configurations (different time units and timezones).
    1025
        default_casted_type: NativeType,
    1026
    }
    1027
    1028
    impl Display for ImplicitCoercion {
    1029
    0
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    1030
    0
            write!(
    1031
    0
                f,
    1032
    0
                "ImplicitCoercion({:?}, default_type={:?})",
    1033
                self.allowed_source_types, self.default_casted_type
    1034
            )
    1035
    0
        }
    1036
    }
    1037
    1038
    impl PartialEq for ImplicitCoercion {
    1039
    0
        fn eq(&self, other: &Self) -> bool {
    1040
    0
            self.allowed_source_types == other.allowed_source_types
    1041
    0
                && self.default_casted_type == other.default_casted_type
    1042
    0
        }
    1043
    }
    1044
    1045
    impl Hash for ImplicitCoercion {
    1046
    0
        fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    1047
    0
            self.allowed_source_types.hash(state);
    1048
    0
            self.default_casted_type.hash(state);
    1049
    0
        }
    1050
    }
    1051
    1052
    /// Provides  information necessary for calling a function.
    1053
    ///
    1054
    /// - [`TypeSignature`] defines the argument types that a function has implementations
    1055
    ///   for.
    1056
    ///
    1057
    /// - [`Volatility`] defines how the output of the function changes with the input.
    1058
    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)]
    1059
    pub struct Signature {
    1060
        /// The data types that the function accepts. See [TypeSignature] for more information.
    1061
        pub type_signature: TypeSignature,
    1062
        /// The volatility of the function. See [Volatility] for more information.
    1063
        pub volatility: Volatility,
    1064
        /// Optional parameter names for the function arguments.
    1065
        ///
    1066
        /// If provided, enables named argument notation for function calls (e.g., `func(a => 1, b => 2)`).
    1067
        /// The length must match the number of arguments defined by `type_signature`.
    1068
        ///
    1069
        /// Defaults to `None`, meaning only positional arguments are supported.
    1070
        pub parameter_names: Option<Vec<String>>,
    1071
    }
    1072
    1073
    impl Signature {
    1074
        /// Creates a new Signature from a given type signature and volatility.
    1075
    0
        pub fn new(type_signature: TypeSignature, volatility: Volatility) -> Self {
    1076
    0
            Signature {
    1077
    0
                type_signature,
    1078
    0
                volatility,
    1079
    0
                parameter_names: None,
    1080
    0
            }
    1081
    0
        }
    1082
        /// An arbitrary number of arguments with the same type, from those listed in `common_types`.
    1083
    0
        pub fn variadic(common_types: Vec<DataType>, volatility: Volatility) -> Self {
    1084
    0
            Self {
    1085
    0
                type_signature: TypeSignature::Variadic(common_types),
    1086
    0
                volatility,
    1087
    0
                parameter_names: None,
    1088
    0
            }
    1089
    0
        }
    1090
        /// User-defined coercion rules for the function.
    1091
    0
        pub fn user_defined(volatility: Volatility) -> Self {
    1092
    0
            Self {
    1093
    0
                type_signature: TypeSignature::UserDefined,
    1094
    0
                volatility,
    1095
    0
                parameter_names: None,
    1096
    0
            }
    1097
    0
        }
    1098
    1099
        /// A specified number of numeric arguments
    1100
    0
        pub fn numeric(arg_count: usize, volatility: Volatility) -> Self {
    1101
    0
            Self {
    1102
    0
                type_signature: TypeSignature::Numeric(arg_count),
    1103
    0
                volatility,
    1104
    0
                parameter_names: None,
    1105
    0
            }
    1106
    0
        }
    1107
    1108
        /// A specified number of string arguments
    1109
    0
        pub fn string(arg_count: usize, volatility: Volatility) -> Self {
    1110
    0
            Self {
    1111
    0
                type_signature: TypeSignature::String(arg_count),
    1112
    0
                volatility,
    1113
    0
                parameter_names: None,
    1114
    0
            }
    1115
    0
        }
    1116
    1117
        /// An arbitrary number of arguments of any type.
    1118
    0
        pub fn variadic_any(volatility: Volatility) -> Self {
    1119
    0
            Self {
    1120
    0
                type_signature: TypeSignature::VariadicAny,
    1121
    0
                volatility,
    1122
    0
                parameter_names: None,
    1123
    0
            }
    1124
    0
        }
    1125
        /// A fixed number of arguments of the same type, from those listed in `valid_types`.
    1126
    0
        pub fn uniform(
    1127
    0
            arg_count: usize,
    1128
    0
            valid_types: Vec<DataType>,
    1129
    0
            volatility: Volatility,
    1130
    0
        ) -> Self {
    1131
    0
            Self {
    1132
    0
                type_signature: TypeSignature::Uniform(arg_count, valid_types),
    1133
    0
                volatility,
    1134
    0
                parameter_names: None,
    1135
    0
            }
    1136
    0
        }
    1137
        /// Exactly matches the types in `exact_types`, in order.
    1138
    0
        pub fn exact(exact_types: Vec<DataType>, volatility: Volatility) -> Self {
    1139
    0
            Signature {
    1140
    0
                type_signature: TypeSignature::Exact(exact_types),
    1141
    0
                volatility,
    1142
    0
                parameter_names: None,
    1143
    0
            }
    1144
    0
        }
    1145
    1146
        /// Target coerce types in order
    1147
    0
        pub fn coercible(target_types: Vec<Coercion>, volatility: Volatility) -> Self {
    1148
    0
            Self {
    1149
    0
                type_signature: TypeSignature::Coercible(target_types),
    1150
    0
                volatility,
    1151
    0
                parameter_names: None,
    1152
    0
            }
    1153
    0
        }
    1154
    1155
        /// Used for function that expects comparable data types, it will try to coerced all the types into single final one.
    1156
    0
        pub fn comparable(arg_count: usize, volatility: Volatility) -> Self {
    1157
    0
            Self {
    1158
    0
                type_signature: TypeSignature::Comparable(arg_count),
    1159
    0
                volatility,
    1160
    0
                parameter_names: None,
    1161
    0
            }
    1162
    0
        }
    1163
    1164
    0
        pub fn nullary(volatility: Volatility) -> Self {
    1165
    0
            Signature {
    1166
    0
                type_signature: TypeSignature::Nullary,
    1167
    0
                volatility,
    1168
    0
                parameter_names: None,
    1169
    0
            }
    1170
    0
        }
    1171
    1172
        /// A specified number of arguments of any type
    1173
    0
        pub fn any(arg_count: usize, volatility: Volatility) -> Self {
    1174
    0
            Signature {
    1175
    0
                type_signature: TypeSignature::Any(arg_count),
    1176
    0
                volatility,
    1177
    0
                parameter_names: None,
    1178
    0
            }
    1179
    0
        }
    1180
    1181
        /// Any one of a list of [TypeSignature]s.
    1182
    0
        pub fn one_of(type_signatures: Vec<TypeSignature>, volatility: Volatility) -> Self {
    1183
    0
            Signature {
    1184
    0
                type_signature: TypeSignature::OneOf(type_signatures),
    1185
    0
                volatility,
    1186
    0
                parameter_names: None,
    1187
    0
            }
    1188
    0
        }
    1189
    1190
        /// Specialized [Signature] for ArrayAppend and similar functions.
    1191
    0
        pub fn array_and_element(volatility: Volatility) -> Self {
    1192
    0
            Signature {
    1193
    0
                type_signature: TypeSignature::ArraySignature(
    1194
    0
                    ArrayFunctionSignature::Array {
    1195
    0
                        arguments: vec![
    1196
    0
                            ArrayFunctionArgument::Array,
    1197
    0
                            ArrayFunctionArgument::Element,
    1198
    0
                        ],
    1199
    0
                        array_coercion: Some(ListCoercion::FixedSizedListToList),
    1200
    0
                    },
    1201
    0
                ),
    1202
    0
                volatility,
    1203
    0
                parameter_names: None,
    1204
    0
            }
    1205
    0
        }
    1206
    1207
        /// Specialized [Signature] for ArrayPrepend and similar functions.
    1208
    0
        pub fn element_and_array(volatility: Volatility) -> Self {
    1209
    0
            Signature {
    1210
    0
                type_signature: TypeSignature::ArraySignature(
    1211
    0
                    ArrayFunctionSignature::Array {
    1212
    0
                        arguments: vec![
    1213
    0
                            ArrayFunctionArgument::Element,
    1214
    0
                            ArrayFunctionArgument::Array,
    1215
    0
                        ],
    1216
    0
                        array_coercion: Some(ListCoercion::FixedSizedListToList),
    1217
    0
                    },
    1218
    0
                ),
    1219
    0
                volatility,
    1220
    0
                parameter_names: None,
    1221
    0
            }
    1222
    0
        }
    1223
    1224
        /// Specialized [Signature] for functions that take a fixed number of arrays.
    1225
    0
        pub fn arrays(
    1226
    0
            n: usize,
    1227
    0
            coercion: Option<ListCoercion>,
    1228
    0
            volatility: Volatility,
    1229
    0
        ) -> Self {
    1230
    0
            Signature {
    1231
    0
                type_signature: TypeSignature::ArraySignature(
    1232
    0
                    ArrayFunctionSignature::Array {
    1233
    0
                        arguments: vec![ArrayFunctionArgument::Array; n],
    1234
    0
                        array_coercion: coercion,
    1235
    0
                    },
    1236
    0
                ),
    1237
    0
                volatility,
    1238
    0
                parameter_names: None,
    1239
    0
            }
    1240
    0
        }
    1241
    1242
        /// Specialized [Signature] for Array functions with an optional index.
    1243
    0
        pub fn array_and_element_and_optional_index(volatility: Volatility) -> Self {
    1244
    0
            Signature {
    1245
    0
                type_signature: TypeSignature::OneOf(vec![
    1246
    0
                    TypeSignature::ArraySignature(ArrayFunctionSignature::Array {
    1247
    0
                        arguments: vec![
    1248
    0
                            ArrayFunctionArgument::Array,
    1249
    0
                            ArrayFunctionArgument::Element,
    1250
    0
                        ],
    1251
    0
                        array_coercion: Some(ListCoercion::FixedSizedListToList),
    1252
    0
                    }),
    1253
    0
                    TypeSignature::ArraySignature(ArrayFunctionSignature::Array {
    1254
    0
                        arguments: vec![
    1255
    0
                            ArrayFunctionArgument::Array,
    1256
    0
                            ArrayFunctionArgument::Element,
    1257
    0
                            ArrayFunctionArgument::Index,
    1258
    0
                        ],
    1259
    0
                        array_coercion: Some(ListCoercion::FixedSizedListToList),
    1260
    0
                    }),
    1261
    0
                ]),
    1262
    0
                volatility,
    1263
    0
                parameter_names: None,
    1264
    0
            }
    1265
    0
        }
    1266
    1267
        /// Specialized [Signature] for ArrayElement and similar functions.
    1268
    0
        pub fn array_and_index(volatility: Volatility) -> Self {
    1269
    0
            Signature {
    1270
    0
                type_signature: TypeSignature::ArraySignature(
    1271
    0
                    ArrayFunctionSignature::Array {
    1272
    0
                        arguments: vec![
    1273
    0
                            ArrayFunctionArgument::Array,
    1274
    0
                            ArrayFunctionArgument::Index,
    1275
    0
                        ],
    1276
    0
                        array_coercion: Some(ListCoercion::FixedSizedListToList),
    1277
    0
                    },
    1278
    0
                ),
    1279
    0
                volatility,
    1280
    0
                parameter_names: None,
    1281
    0
            }
    1282
    0
        }
    1283
    1284
        /// Specialized [Signature] for ArrayEmpty and similar functions.
    1285
    0
        pub fn array(volatility: Volatility) -> Self {
    1286
    0
            Signature::arrays(1, Some(ListCoercion::FixedSizedListToList), volatility)
    1287
    0
        }
    1288
    1289
        /// Add parameter names to this signature, enabling named argument notation.
    1290
        ///
    1291
        /// # Example
    1292
        /// ```
    1293
        /// # use datafusion_expr_common::signature::{Signature, Volatility};
    1294
        /// # use arrow::datatypes::DataType;
    1295
        /// let sig =
    1296
        ///     Signature::exact(vec![DataType::Int32, DataType::Utf8], Volatility::Immutable)
    1297
        ///         .with_parameter_names(vec!["count".to_string(), "name".to_string()]);
    1298
        /// ```
    1299
        ///
    1300
        /// # Errors
    1301
        /// Returns an error if the number of parameter names doesn't match the signature's arity.
    1302
        /// For signatures with variable arity (e.g., `Variadic`, `VariadicAny`), parameter names
    1303
        /// cannot be specified.
    1304
    0
        pub fn with_parameter_names(mut self, names: Vec<impl Into<String>>) -> Result<Self> {
    1305
    0
            let names = names.into_iter().map(Into::into).collect::<Vec<String>>();
    1306
            // Validate that the number of names matches the signature
    1307
    0
            self.validate_parameter_names(&names)?;
    1308
    0
            self.parameter_names = Some(names);
    1309
    0
            Ok(self)
    1310
    0
        }
    1311
    1312
        /// Validate that parameter names are compatible with this signature
    1313
    0
        fn validate_parameter_names(&self, names: &[String]) -> Result<()> {
    1314
    0
            match self.type_signature.arity() {
    1315
    0
                Arity::Fixed(expected) => {
    1316
    0
                    if names.len() != expected {
    1317
    0
                        return plan_err!(
    1318
    0
                            "Parameter names count ({}) does not match signature arity ({})",
    1319
    0
                            names.len(),
    1320
                            expected
    1321
                        );
    1322
    0
                    }
    1323
                }
    1324
                Arity::Variable => {
    1325
                    // For UserDefined signatures, allow parameter names
    1326
                    // The function implementer is responsible for validating the names match the actual arguments
    1327
    0
                    if !matches!(self.type_signature, TypeSignature::UserDefined) {
    1328
    0
                        return plan_err!(
    1329
    0
                            "Cannot specify parameter names for variable arity signature: {:?}",
    1330
                            self.type_signature
    1331
                        );
    1332
    0
                    }
    1333
                }
    1334
            }
    1335
    1336
    0
            let mut seen = std::collections::HashSet::new();
    1337
    0
            for name in names {
    1338
    0
                if !seen.insert(name) {
    1339
    0
                    return plan_err!("Duplicate parameter name: '{}'", name);
    1340
    0
                }
    1341
            }
    1342
    1343
    0
            Ok(())
    1344
    0
        }
    1345
    }
    1346
    1347
    #[cfg(test)]
    1348
    mod tests {
    1349
        use datafusion_common::types::{logical_int32, logical_int64, logical_string};
    1350
    1351
        use super::*;
    1352
        use crate::signature::{
    1353
            ArrayFunctionArgument, ArrayFunctionSignature, Coercion, TypeSignatureClass,
    1354
        };
    1355
    1356
        #[test]
    1357
        fn supports_zero_argument_tests() {
    1358
            // Testing `TypeSignature`s which supports 0 arg
    1359
            let positive_cases = vec![
    1360
                TypeSignature::Exact(vec![]),
    1361
                TypeSignature::OneOf(vec![
    1362
                    TypeSignature::Exact(vec![DataType::Int8]),
    1363
                    TypeSignature::Nullary,
    1364
                    TypeSignature::Uniform(1, vec![DataType::Int8]),
    1365
                ]),
    1366
                TypeSignature::Nullary,
    1367
            ];
    1368
    1369
            for case in positive_cases {
    1370
                assert!(
    1371
                    case.supports_zero_argument(),
    1372
                    "Expected {case:?} to support zero arguments"
    1373
                );
    1374
            }
    1375
    1376
            // Testing `TypeSignature`s which doesn't support 0 arg
    1377
            let negative_cases = vec![
    1378
                TypeSignature::Exact(vec![DataType::Utf8]),
    1379
                TypeSignature::Uniform(1, vec![DataType::Float64]),
    1380
                TypeSignature::Any(1),
    1381
                TypeSignature::VariadicAny,
    1382
                TypeSignature::OneOf(vec![
    1383
                    TypeSignature::Exact(vec![DataType::Int8]),
    1384
                    TypeSignature::Uniform(1, vec![DataType::Int8]),
    1385
                ]),
    1386
            ];
    1387
    1388
            for case in negative_cases {
    1389
                assert!(
    1390
                    !case.supports_zero_argument(),
    1391
                    "Expected {case:?} not to support zero arguments"
    1392
                );
    1393
            }
    1394
        }
    1395
    1396
        #[test]
    1397
        fn type_signature_partial_ord() {
    1398
            // Test validates that partial ord is defined for TypeSignature and Signature.
    1399
            assert!(TypeSignature::UserDefined < TypeSignature::VariadicAny);
    1400
            assert!(TypeSignature::UserDefined < TypeSignature::Any(1));
    1401
    1402
            assert!(
    1403
                TypeSignature::Uniform(1, vec![DataType::Null])
    1404
                    < TypeSignature::Uniform(1, vec![DataType::Boolean])
    1405
            );
    1406
            assert!(
    1407
                TypeSignature::Uniform(1, vec![DataType::Null])
    1408
                    < TypeSignature::Uniform(2, vec![DataType::Null])
    1409
            );
    1410
            assert!(
    1411
                TypeSignature::Uniform(usize::MAX, vec![DataType::Null])
    1412
                    < TypeSignature::Exact(vec![DataType::Null])
    1413
            );
    1414
        }
    1415
    1416
        #[test]
    1417
        fn test_get_possible_types() {
    1418
            let type_signature = TypeSignature::Exact(vec![DataType::Int32, DataType::Int64]);
    1419
            let possible_types = type_signature.get_example_types();
    1420
            assert_eq!(possible_types, vec![vec![DataType::Int32, DataType::Int64]]);
    1421
    1422
            let type_signature = TypeSignature::OneOf(vec![
    1423
                TypeSignature::Exact(vec![DataType::Int32, DataType::Int64]),
    1424
                TypeSignature::Exact(vec![DataType::Float32, DataType::Float64]),
    1425
            ]);
    1426
            let possible_types = type_signature.get_example_types();
    1427
            assert_eq!(
    1428
                possible_types,
    1429
                vec![
    1430
                    vec![DataType::Int32, DataType::Int64],
    1431
                    vec![DataType::Float32, DataType::Float64]
    1432
                ]
    1433
            );
    1434
    1435
            let type_signature = TypeSignature::OneOf(vec![
    1436
                TypeSignature::Exact(vec![DataType::Int32, DataType::Int64]),
    1437
                TypeSignature::Exact(vec![DataType::Float32, DataType::Float64]),
    1438
                TypeSignature::Exact(vec![DataType::Utf8]),
    1439
            ]);
    1440
            let possible_types = type_signature.get_example_types();
    1441
            assert_eq!(
    1442
                possible_types,
    1443
                vec![
    1444
                    vec![DataType::Int32, DataType::Int64],
    1445
                    vec![DataType::Float32, DataType::Float64],
    1446
                    vec![DataType::Utf8]
    1447
                ]
    1448
            );
    1449
    1450
            let type_signature =
    1451
                TypeSignature::Uniform(2, vec![DataType::Float32, DataType::Int64]);
    1452
            let possible_types = type_signature.get_example_types();
    1453
            assert_eq!(
    1454
                possible_types,
    1455
                vec![
    1456
                    vec![DataType::Float32, DataType::Float32],
    1457
                    vec![DataType::Int64, DataType::Int64]
    1458
                ]
    1459
            );
    1460
    1461
            let type_signature = TypeSignature::Coercible(vec![
    1462
                Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
    1463
                Coercion::new_exact(TypeSignatureClass::Native(logical_int64())),
    1464
            ]);
    1465
            let possible_types = type_signature.get_example_types();
    1466
            assert_eq!(
    1467
                possible_types,
    1468
                vec![
    1469
                    vec![DataType::Utf8, DataType::Int64],
    1470
                    vec![DataType::LargeUtf8, DataType::Int64],
    1471
                    vec![DataType::Utf8View, DataType::Int64]
    1472
                ]
    1473
            );
    1474
    1475
            let type_signature =
    1476
                TypeSignature::Variadic(vec![DataType::Int32, DataType::Int64]);
    1477
            let possible_types = type_signature.get_example_types();
    1478
            assert_eq!(
    1479
                possible_types,
    1480
                vec![vec![DataType::Int32], vec![DataType::Int64]]
    1481
            );
    1482
    1483
            let type_signature = TypeSignature::Numeric(2);
    1484
            let possible_types = type_signature.get_example_types();
    1485
            assert_eq!(
    1486
                possible_types,
    1487
                vec![
    1488
                    vec![DataType::Int8, DataType::Int8],
    1489
                    vec![DataType::Int16, DataType::Int16],
    1490
                    vec![DataType::Int32, DataType::Int32],
    1491
                    vec![DataType::Int64, DataType::Int64],
    1492
                    vec![DataType::UInt8, DataType::UInt8],
    1493
                    vec![DataType::UInt16, DataType::UInt16],
    1494
                    vec![DataType::UInt32, DataType::UInt32],
    1495
                    vec![DataType::UInt64, DataType::UInt64],
    1496
                    vec![DataType::Float32, DataType::Float32],
    1497
                    vec![DataType::Float64, DataType::Float64]
    1498
                ]
    1499
            );
    1500
    1501
            let type_signature = TypeSignature::String(2);
    1502
            let possible_types = type_signature.get_example_types();
    1503
            assert_eq!(
    1504
                possible_types,
    1505
                vec![
    1506
                    vec![DataType::Utf8, DataType::Utf8],
    1507
                    vec![DataType::LargeUtf8, DataType::LargeUtf8],
    1508
                    vec![DataType::Utf8View, DataType::Utf8View]
    1509
                ]
    1510
            );
    1511
        }
    1512
    1513
        #[test]
    1514
        fn test_signature_with_parameter_names() {
    1515
            let sig = Signature::exact(
    1516
                vec![DataType::Int32, DataType::Utf8],
    1517
                Volatility::Immutable,
    1518
            )
    1519
            .with_parameter_names(vec!["count".to_string(), "name".to_string()])
    1520
            .unwrap();
    1521
    1522
            assert_eq!(
    1523
                sig.parameter_names,
    1524
                Some(vec!["count".to_string(), "name".to_string()])
    1525
            );
    1526
            assert_eq!(
    1527
                sig.type_signature,
    1528
                TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8])
    1529
            );
    1530
        }
    1531
    1532
        #[test]
    1533
        fn test_signature_parameter_names_wrong_count() {
    1534
            let result = Signature::exact(
    1535
                vec![DataType::Int32, DataType::Utf8],
    1536
                Volatility::Immutable,
    1537
            )
    1538
            .with_parameter_names(vec!["count".to_string()]); // Only 1 name for 2 args
    1539
    1540
            assert!(result.is_err());
    1541
            assert!(result
    1542
                .unwrap_err()
    1543
                .to_string()
    1544
                .contains("does not match signature arity"));
    1545
        }
    1546
    1547
        #[test]
    1548
        fn test_signature_parameter_names_duplicate() {
    1549
            let result = Signature::exact(
    1550
                vec![DataType::Int32, DataType::Int32],
    1551
                Volatility::Immutable,
    1552
            )
    1553
            .with_parameter_names(vec!["count".to_string(), "count".to_string()]);
    1554
    1555
            assert!(result.is_err());
    1556
            assert!(result
    1557
                .unwrap_err()
    1558
                .to_string()
    1559
                .contains("Duplicate parameter name"));
    1560
        }
    1561
    1562
        #[test]
    1563
        fn test_signature_parameter_names_variadic() {
    1564
            let result = Signature::variadic(vec![DataType::Int32], Volatility::Immutable)
    1565
                .with_parameter_names(vec!["arg".to_string()]);
    1566
    1567
            assert!(result.is_err());
    1568
            assert!(result
    1569
                .unwrap_err()
    1570
                .to_string()
    1571
                .contains("variable arity signature"));
    1572
        }
    1573
    1574
        #[test]
    1575
        fn test_signature_without_parameter_names() {
    1576
            let sig = Signature::exact(
    1577
                vec![DataType::Int32, DataType::Utf8],
    1578
                Volatility::Immutable,
    1579
            );
    1580
    1581
            assert_eq!(sig.parameter_names, None);
    1582
        }
    1583
    1584
        #[test]
    1585
        fn test_signature_uniform_with_parameter_names() {
    1586
            let sig = Signature::uniform(3, vec![DataType::Float64], Volatility::Immutable)
    1587
                .with_parameter_names(vec!["x".to_string(), "y".to_string(), "z".to_string()])
    1588
                .unwrap();
    1589
    1590
            assert_eq!(
    1591
                sig.parameter_names,
    1592
                Some(vec!["x".to_string(), "y".to_string(), "z".to_string()])
    1593
            );
    1594
        }
    1595
    1596
        #[test]
    1597
        fn test_signature_numeric_with_parameter_names() {
    1598
            let sig = Signature::numeric(2, Volatility::Immutable)
    1599
                .with_parameter_names(vec!["a".to_string(), "b".to_string()])
    1600
                .unwrap();
    1601
    1602
            assert_eq!(
    1603
                sig.parameter_names,
    1604
                Some(vec!["a".to_string(), "b".to_string()])
    1605
            );
    1606
        }
    1607
    1608
        #[test]
    1609
        fn test_signature_nullary_with_empty_names() {
    1610
            let sig = Signature::nullary(Volatility::Immutable)
    1611
                .with_parameter_names(Vec::<String>::new())
    1612
                .unwrap();
    1613
    1614
            assert_eq!(sig.parameter_names, Some(vec![]));
    1615
        }
    1616
    1617
        #[test]
    1618
        fn test_to_string_repr_with_names_exact() {
    1619
            let sig = TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8]);
    1620
    1621
            assert_eq!(sig.to_string_repr_with_names(None), vec!["Int32, Utf8"]);
    1622
    1623
            let names = vec!["id".to_string(), "name".to_string()];
    1624
            assert_eq!(
    1625
                sig.to_string_repr_with_names(Some(&names)),
    1626
                vec!["id: Int32, name: Utf8"]
    1627
            );
    1628
        }
    1629
    1630
        #[test]
    1631
        fn test_to_string_repr_with_names_any() {
    1632
            let sig = TypeSignature::Any(3);
    1633
    1634
            assert_eq!(sig.to_string_repr_with_names(None), vec!["Any, Any, Any"]);
    1635
    1636
            let names = vec!["x".to_string(), "y".to_string(), "z".to_string()];
    1637
            assert_eq!(
    1638
                sig.to_string_repr_with_names(Some(&names)),
    1639
                vec!["x: Any, y: Any, z: Any"]
    1640
            );
    1641
        }
    1642
    1643
        #[test]
    1644
        fn test_to_string_repr_with_names_one_of() {
    1645
            let sig =
    1646
                TypeSignature::OneOf(vec![TypeSignature::Any(2), TypeSignature::Any(3)]);
    1647
    1648
            assert_eq!(
    1649
                sig.to_string_repr_with_names(None),
    1650
                vec!["Any, Any", "Any, Any, Any"]
    1651
            );
    1652
    1653
            let names = vec![
    1654
                "str".to_string(),
    1655
                "start_pos".to_string(),
    1656
                "length".to_string(),
    1657
            ];
    1658
            assert_eq!(
    1659
                sig.to_string_repr_with_names(Some(&names)),
    1660
                vec![
    1661
                    "str: Any, start_pos: Any",
    1662
                    "str: Any, start_pos: Any, length: Any"
    1663
                ]
    1664
            );
    1665
        }
    1666
    1667
        #[test]
    1668
        fn test_to_string_repr_with_names_partial() {
    1669
            // This simulates providing max arity names for a OneOf signature
    1670
            let sig = TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8]);
    1671
    1672
            // Provide 3 names for 2-parameter signature (extra name is ignored via zip)
    1673
            let names = vec!["a".to_string(), "b".to_string(), "c".to_string()];
    1674
            assert_eq!(
    1675
                sig.to_string_repr_with_names(Some(&names)),
    1676
                vec!["a: Int32, b: Utf8"]
    1677
            );
    1678
        }
    1679
    1680
        #[test]
    1681
        fn test_to_string_repr_with_names_uniform() {
    1682
            let sig = TypeSignature::Uniform(2, vec![DataType::Float64]);
    1683
    1684
            assert_eq!(
    1685
                sig.to_string_repr_with_names(None),
    1686
                vec!["Float64, Float64"]
    1687
            );
    1688
    1689
            let names = vec!["x".to_string(), "y".to_string()];
    1690
            assert_eq!(
    1691
                sig.to_string_repr_with_names(Some(&names)),
    1692
                vec!["x: Float64, y: Float64"]
    1693
            );
    1694
        }
    1695
    1696
        #[test]
    1697
        fn test_to_string_repr_with_names_coercible() {
    1698
            let sig = TypeSignature::Coercible(vec![
    1699
                Coercion::new_exact(TypeSignatureClass::Native(logical_int32())),
    1700
                Coercion::new_exact(TypeSignatureClass::Native(logical_int32())),
    1701
            ]);
    1702
    1703
            let names = vec!["a".to_string(), "b".to_string()];
    1704
            let result = sig.to_string_repr_with_names(Some(&names));
    1705
            // Check that it contains the parameter names with type annotations
    1706
            assert_eq!(result.len(), 1);
    1707
            assert!(result[0].starts_with("a: "));
    1708
            assert!(result[0].contains(", b: "));
    1709
        }
    1710
    1711
        #[test]
    1712
        fn test_to_string_repr_with_names_comparable_numeric_string() {
    1713
            let comparable = TypeSignature::Comparable(3);
    1714
            let numeric = TypeSignature::Numeric(2);
    1715
            let string_sig = TypeSignature::String(2);
    1716
    1717
            let names = vec!["a".to_string(), "b".to_string(), "c".to_string()];
    1718
    1719
            // All should show parameter names with type annotations
    1720
            assert_eq!(
    1721
                comparable.to_string_repr_with_names(Some(&names)),
    1722
                vec!["a: Comparable, b: Comparable, c: Comparable"]
    1723
            );
    1724
            assert_eq!(
    1725
                numeric.to_string_repr_with_names(Some(&names)),
    1726
                vec!["a: Numeric, b: Numeric"]
    1727
            );
    1728
            assert_eq!(
    1729
                string_sig.to_string_repr_with_names(Some(&names)),
    1730
                vec!["a: String, b: String"]
    1731
            );
    1732
        }
    1733
    1734
        #[test]
    1735
        fn test_to_string_repr_with_names_variadic_fallback() {
    1736
            let variadic = TypeSignature::Variadic(vec![DataType::Utf8, DataType::LargeUtf8]);
    1737
            let names = vec!["x".to_string()];
    1738
            assert_eq!(
    1739
                variadic.to_string_repr_with_names(Some(&names)),
    1740
                variadic.to_string_repr()
    1741
            );
    1742
    1743
            let variadic_any = TypeSignature::VariadicAny;
    1744
            assert_eq!(
    1745
                variadic_any.to_string_repr_with_names(Some(&names)),
    1746
                variadic_any.to_string_repr()
    1747
            );
    1748
    1749
            // UserDefined now shows parameter names when available
    1750
            let user_defined = TypeSignature::UserDefined;
    1751
            assert_eq!(
    1752
                user_defined.to_string_repr_with_names(Some(&names)),
    1753
                vec!["x"]
    1754
            );
    1755
            assert_eq!(
    1756
                user_defined.to_string_repr_with_names(None),
    1757
                user_defined.to_string_repr()
    1758
            );
    1759
        }
    1760
    1761
        #[test]
    1762
        fn test_to_string_repr_with_names_nullary() {
    1763
            let sig = TypeSignature::Nullary;
    1764
            let names = vec!["x".to_string()];
    1765
    1766
            // Should return empty representation, names don't apply
    1767
            assert_eq!(
    1768
                sig.to_string_repr_with_names(Some(&names)),
    1769
                vec!["NullAry()"]
    1770
            );
    1771
            assert_eq!(sig.to_string_repr_with_names(None), vec!["NullAry()"]);
    1772
        }
    1773
    1774
        #[test]
    1775
        fn test_to_string_repr_with_names_array_signature() {
    1776
            let sig = TypeSignature::ArraySignature(ArrayFunctionSignature::Array {
    1777
                arguments: vec![
    1778
                    ArrayFunctionArgument::Array,
    1779
                    ArrayFunctionArgument::Index,
    1780
                    ArrayFunctionArgument::Element,
    1781
                ],
    1782
                array_coercion: None,
    1783
            });
    1784
    1785
            assert_eq!(
    1786
                sig.to_string_repr_with_names(None),
    1787
                vec!["array, index, element"]
    1788
            );
    1789
    1790
            let names = vec!["arr".to_string(), "idx".to_string(), "val".to_string()];
    1791
            assert_eq!(
    1792
                sig.to_string_repr_with_names(Some(&names)),
    1793
                vec!["arr: array, idx: index, val: element"]
    1794
            );
    1795
    1796
            let recursive =
    1797
                TypeSignature::ArraySignature(ArrayFunctionSignature::RecursiveArray);
    1798
            let names = vec!["array".to_string()];
    1799
            assert_eq!(
    1800
                recursive.to_string_repr_with_names(Some(&names)),
    1801
                vec!["array: recursive_array"]
    1802
            );
    1803
    1804
            // Test MapArray (1 argument)
    1805
            let map_array = TypeSignature::ArraySignature(ArrayFunctionSignature::MapArray);
    1806
            let names = vec!["map".to_string()];
    1807
            assert_eq!(
    1808
                map_array.to_string_repr_with_names(Some(&names)),
    1809
                vec!["map: map_array"]
    1810
            );
    1811
        }
    1812
    1813
        #[test]
    1814
        fn test_type_signature_arity_exact() {
    1815
            let sig = TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8]);
    1816
            assert_eq!(sig.arity(), Arity::Fixed(2));
    1817
    1818
            let sig = TypeSignature::Exact(vec![]);
    1819
            assert_eq!(sig.arity(), Arity::Fixed(0));
    1820
        }
    1821
    1822
        #[test]
    1823
        fn test_type_signature_arity_uniform() {
    1824
            let sig = TypeSignature::Uniform(3, vec![DataType::Float64]);
    1825
            assert_eq!(sig.arity(), Arity::Fixed(3));
    1826
    1827
            let sig = TypeSignature::Uniform(1, vec![DataType::Int32]);
    1828
            assert_eq!(sig.arity(), Arity::Fixed(1));
    1829
        }
    1830
    1831
        #[test]
    1832
        fn test_type_signature_arity_numeric() {
    1833
            let sig = TypeSignature::Numeric(2);
    1834
            assert_eq!(sig.arity(), Arity::Fixed(2));
    1835
        }
    1836
    1837
        #[test]
    1838
        fn test_type_signature_arity_string() {
    1839
            let sig = TypeSignature::String(3);
    1840
            assert_eq!(sig.arity(), Arity::Fixed(3));
    1841
        }
    1842
    1843
        #[test]
    1844
        fn test_type_signature_arity_comparable() {
    1845
            let sig = TypeSignature::Comparable(2);
    1846
            assert_eq!(sig.arity(), Arity::Fixed(2));
    1847
        }
    1848
    1849
        #[test]
    1850
        fn test_type_signature_arity_any() {
    1851
            let sig = TypeSignature::Any(4);
    1852
            assert_eq!(sig.arity(), Arity::Fixed(4));
    1853
        }
    1854
    1855
        #[test]
    1856
        fn test_type_signature_arity_coercible() {
    1857
            let sig = TypeSignature::Coercible(vec![
    1858
                Coercion::new_exact(TypeSignatureClass::Native(logical_int32())),
    1859
                Coercion::new_exact(TypeSignatureClass::Native(logical_string())),
    1860
            ]);
    1861
            assert_eq!(sig.arity(), Arity::Fixed(2));
    1862
        }
    1863
    1864
        #[test]
    1865
        fn test_type_signature_arity_nullary() {
    1866
            let sig = TypeSignature::Nullary;
    1867
            assert_eq!(sig.arity(), Arity::Fixed(0));
    1868
        }
    1869
    1870
        #[test]
    1871
        fn test_type_signature_arity_array_signature() {
    1872
            // Test Array variant with 2 arguments
    1873
            let sig = TypeSignature::ArraySignature(ArrayFunctionSignature::Array {
    1874
                arguments: vec![ArrayFunctionArgument::Array, ArrayFunctionArgument::Index],
    1875
                array_coercion: None,
    1876
            });
    1877
            assert_eq!(sig.arity(), Arity::Fixed(2));
    1878
    1879
            // Test Array variant with 3 arguments
    1880
            let sig = TypeSignature::ArraySignature(ArrayFunctionSignature::Array {
    1881
                arguments: vec![
    1882
                    ArrayFunctionArgument::Array,
    1883
                    ArrayFunctionArgument::Element,
    1884
                    ArrayFunctionArgument::Index,
    1885
                ],
    1886
                array_coercion: None,
    1887
            });
    1888
            assert_eq!(sig.arity(), Arity::Fixed(3));
    1889
    1890
            // Test RecursiveArray variant
    1891
            let sig = TypeSignature::ArraySignature(ArrayFunctionSignature::RecursiveArray);
    1892
            assert_eq!(sig.arity(), Arity::Fixed(1));
    1893
    1894
            // Test MapArray variant
    1895
            let sig = TypeSignature::ArraySignature(ArrayFunctionSignature::MapArray);
    1896
            assert_eq!(sig.arity(), Arity::Fixed(1));
    1897
        }
    1898
    1899
        #[test]
    1900
        fn test_type_signature_arity_one_of_fixed() {
    1901
            // OneOf with all fixed arity variants should return max arity
    1902
            let sig = TypeSignature::OneOf(vec![
    1903
                TypeSignature::Exact(vec![DataType::Int32]),
    1904
                TypeSignature::Exact(vec![DataType::Int32, DataType::Utf8]),
    1905
                TypeSignature::Exact(vec![
    1906
                    DataType::Int32,
    1907
                    DataType::Utf8,
    1908
                    DataType::Float64,
    1909
                ]),
    1910
            ]);
    1911
            assert_eq!(sig.arity(), Arity::Fixed(3));
    1912
        }
    1913
    1914
        #[test]
    1915
        fn test_type_signature_arity_one_of_variable() {
    1916
            // OneOf with variable arity variant should return Variable
    1917
            let sig = TypeSignature::OneOf(vec![
    1918
                TypeSignature::Exact(vec![DataType::Int32]),
    1919
                TypeSignature::VariadicAny,
    1920
            ]);
    1921
            assert_eq!(sig.arity(), Arity::Variable);
    1922
        }
    1923
    1924
        #[test]
    1925
        fn test_type_signature_arity_variadic() {
    1926
            let sig = TypeSignature::Variadic(vec![DataType::Int32]);
    1927
            assert_eq!(sig.arity(), Arity::Variable);
    1928
    1929
            let sig = TypeSignature::VariadicAny;
    1930
            assert_eq!(sig.arity(), Arity::Variable);
    1931
        }
    1932
    1933
        #[test]
    1934
        fn test_type_signature_arity_user_defined() {
    1935
            let sig = TypeSignature::UserDefined;
    1936
            assert_eq!(sig.arity(), Arity::Variable);
    1937
        }
    1938
    }