## 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/functions/src/math/power.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
    //! Math function: `power()`.
    19
    use std::any::Any;
    20
    21
    use super::log::LogFunc;
    22
    23
    use crate::utils::{calculate_binary_decimal_math, calculate_binary_math};
    24
    use arrow::array::{Array, ArrayRef};
    25
    use arrow::datatypes::{
    26
        ArrowNativeTypeOp, DataType, Decimal128Type, Decimal256Type, Decimal32Type,
    27
        Decimal64Type, Float64Type, Int64Type,
    28
    };
    29
    use arrow::error::ArrowError;
    30
    use datafusion_common::utils::take_function_args;
    31
    use datafusion_common::{exec_err, plan_datafusion_err, Result, ScalarValue};
    32
    use datafusion_expr::expr::ScalarFunction;
    33
    use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
    34
    use datafusion_expr::type_coercion::is_decimal;
    35
    use datafusion_expr::{
    36
        ColumnarValue, Documentation, Expr, ScalarFunctionArgs, ScalarUDF,
    37
    };
    38
    use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
    39
    use datafusion_macros::user_doc;
    40
    41
    #[user_doc(
    42
        doc_section(label = "Math Functions"),
    43
        description = "Returns a base expression raised to the power of an exponent.",
    44
        syntax_example = "power(base, exponent)",
    45
        sql_example = r#"```sql
    46
    > SELECT power(2, 3);
    47
    +-------------+
    48
    | power(2,3)  |
    49
    +-------------+
    50
    | 8           |
    51
    +-------------+
    52
    ```"#,
    53
        standard_argument(name = "base", prefix = "Numeric"),
    54
        standard_argument(name = "exponent", prefix = "Exponent numeric")
    55
    )]
    56
    #[derive(Debug, PartialEq, Eq, Hash)]
    57
    pub struct PowerFunc {
    58
        signature: Signature,
    59
        aliases: Vec<String>,
    60
    }
    61
    62
    impl Default for PowerFunc {
    63
    0
        fn default() -> Self {
    64
    0
            Self::new()
    65
    0
        }
    66
    }
    67
    68
    impl PowerFunc {
    69
    0
        pub fn new() -> Self {
    70
    0
            Self {
    71
    0
                signature: Signature::user_defined(Volatility::Immutable),
    72
    0
                aliases: vec![String::from("pow")],
    73
    0
            }
    74
    0
        }
    75
    }
    76
    77
    /// Binary function to calculate a math power to integer exponent
    78
    /// for scaled integer types.
    79
    ///
    80
    /// Formula
    81
    /// The power for a scaled integer `b` is
    82
    ///
    83
    /// ```text
    84
    /// (b * 10^(-s)) ^ e
    85
    /// ```
    86
    /// However, the result should be scaled back from scale 0 to scale `s`,
    87
    /// which is done by multiplying by `10^s`.
    88
    /// At the end, the formula is:
    89
    ///
    90
    /// ```text
    91
    ///   b^e * 10^(-s * e) * 10^s = b^e / 10^(s * (e-1))
    92
    /// ```
    93
    /// Example of 2.5 ^ 4 = 39:
    94
    ///   2.5 is represented as 25 with scale 1
    95
    ///   The unscaled result is 25^4 = 390625
    96
    ///   Scale it back to 1: 390625 / 10^4 = 39
    97
    ///
    98
    /// Returns error if base is invalid
    99
    0
    fn pow_decimal_int<T>(base: T, scale: i8, exp: i64) -> Result<T, ArrowError>
    100
    0
    where
    101
    0
        T: From<i32> + ArrowNativeTypeOp,
    102
    {
    103
    0
        let scale: u32 = scale.try_into().map_err(|_| {
    104
    0
            ArrowError::NotYetImplemented(format!(
    105
    0
                "Negative scale is not yet supported value: {scale}"
    106
    0
            ))
    107
    0
        })?;
    108
    0
        if exp == 0 {
    109
            // Edge case to provide 1 as result (10^s with scale)
    110
    0
            let result: T = T::from(10).pow_checked(scale).map_err(|_| {
    111
    0
                ArrowError::ArithmeticOverflow(format!(
    112
    0
                    "Cannot make unscale factor for {scale} and {exp}"
    113
    0
                ))
    114
    0
            })?;
    115
    0
            return Ok(result);
    116
    0
        }
    117
    0
        let exp: u32 = exp.try_into().map_err(|_| {
    118
    0
            ArrowError::ArithmeticOverflow(format!("Unsupported exp value: {exp}"))
    119
    0
        })?;
    120
    0
        let powered: T = base.pow_checked(exp).map_err(|_| {
    121
    0
            ArrowError::ArithmeticOverflow(format!("Cannot raise base {base:?} to exp {exp}"))
    122
    0
        })?;
    123
    0
        let unscale_factor: T = T::from(10).pow_checked(scale * (exp - 1)).map_err(|_| {
    124
    0
            ArrowError::ArithmeticOverflow(format!(
    125
    0
                "Cannot make unscale factor for {scale} and {exp}"
    126
    0
            ))
    127
    0
        })?;
    128
    129
    0
        powered.div_checked(unscale_factor)
    130
    0
    }
    131
    132
    /// Binary function to calculate a math power to float exponent
    133
    /// for scaled integer types.
    134
    /// Returns error if exponent is negative or non-integer, or base invalid
    135
    0
    fn pow_decimal_float<T>(base: T, scale: i8, exp: f64) -> Result<T, ArrowError>
    136
    0
    where
    137
    0
        T: From<i32> + ArrowNativeTypeOp,
    138
    {
    139
    0
        if !exp.is_finite() || exp.trunc() != exp {
    140
    0
            return Err(ArrowError::ComputeError(format!(
    141
    0
                "Cannot use non-integer exp: {exp}"
    142
    0
            )));
    143
    0
        }
    144
    0
        if exp < 0f64 || exp >= u32::MAX as f64 {
    145
    0
            return Err(ArrowError::ArithmeticOverflow(format!(
    146
    0
                "Unsupported exp value: {exp}"
    147
    0
            )));
    148
    0
        }
    149
    0
        pow_decimal_int(base, scale, exp as i64)
    150
    0
    }
    151
    152
    impl ScalarUDFImpl for PowerFunc {
    153
    0
        fn as_any(&self) -> &dyn Any {
    154
    0
            self
    155
    0
        }
    156
    0
        fn name(&self) -> &str {
    157
    0
            "power"
    158
    0
        }
    159
    160
    0
        fn signature(&self) -> &Signature {
    161
    0
            &self.signature
    162
    0
        }
    163
    164
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    165
    0
            Ok(arg_types[0].clone())
    166
    0
        }
    167
    168
    0
        fn aliases(&self) -> &[String] {
    169
    0
            &self.aliases
    170
    0
        }
    171
    172
    0
        fn coerce_types(&self, arg_types: &[DataType]) -> Result<Vec<DataType>> {
    173
    0
            let [arg1, arg2] = take_function_args(self.name(), arg_types)?;
    174
    175
    0
            fn coerced_type_base(name: &str, data_type: &DataType) -> Result<DataType> {
    176
    0
                match data_type {
    177
    0
                    DataType::Null => Ok(DataType::Int64),
    178
    0
                    d if d.is_floating() => Ok(DataType::Float64),
    179
    0
                    d if d.is_integer() => Ok(DataType::Int64),
    180
    0
                    d if is_decimal(d) => Ok(d.clone()),
    181
    0
                    other => {
    182
    0
                        exec_err!("Unsupported data type {other:?} for {} function", name)
    183
                    }
    184
                }
    185
    0
            }
    186
    187
    0
            fn coerced_type_exp(name: &str, data_type: &DataType) -> Result<DataType> {
    188
    0
                match data_type {
    189
    0
                    DataType::Null => Ok(DataType::Int64),
    190
    0
                    d if d.is_floating() => Ok(DataType::Float64),
    191
    0
                    d if d.is_integer() => Ok(DataType::Int64),
    192
    0
                    d if is_decimal(d) => Ok(DataType::Float64),
    193
    0
                    other => {
    194
    0
                        exec_err!("Unsupported data type {other:?} for {} function", name)
    195
                    }
    196
                }
    197
    0
            }
    198
    199
    0
            Ok(vec![
    200
    0
                coerced_type_base(self.name(), arg1)?,
    201
    0
                coerced_type_exp(self.name(), arg2)?,
    202
            ])
    203
    0
        }
    204
    205
    0
        fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
    206
    0
            let base = &args.args[0].to_array(args.number_rows)?;
    207
    0
            let exponent = &args.args[1];
    208
    209
    0
            let arr: ArrayRef = match (base.data_type(), exponent.data_type()) {
    210
                (DataType::Float64, _) => {
    211
    0
                    calculate_binary_math::<Float64Type, Float64Type, Float64Type, _>(
    212
    0
                        &base,
    213
    0
                        exponent,
    214
    0
                        |b, e| Ok(f64::powf(b, e)),
    215
    0
                    )?
    216
                }
    217
                (DataType::Int64, _) => {
    218
    0
                    calculate_binary_math::<Int64Type, Int64Type, Int64Type, _>(
    219
    0
                        &base,
    220
    0
                        exponent,
    221
    0
                        |b, e| match e.try_into() {
    222
    0
                            Ok(exp_u32) => b.pow_checked(exp_u32),
    223
    0
                            Err(_) => Err(ArrowError::ArithmeticOverflow(format!(
    224
    0
                                "Exponent {e} in integer computation is out of bounds."
    225
    0
                            ))),
    226
    0
                        },
    227
    0
                    )?
    228
                }
    229
    0
                (DataType::Decimal32(precision, scale), DataType::Int64) =>
    230
    0
                        calculate_binary_decimal_math::<Decimal32Type, Int64Type, Decimal32Type, _>(
    231
    0
                            &base,
    232
    0
                            exponent,
    233
    0
                            |b, e| pow_decimal_int(b, *scale, e),
    234
    0
                        *precision,
    235
    0
                        *scale,
    236
    0
                    )?,
    237
    0
                (DataType::Decimal32(precision, scale), DataType::Float64) =>
    238
    0
                        calculate_binary_decimal_math::<Decimal32Type, Float64Type, Decimal32Type, _>(
    239
    0
                            &base,
    240
    0
                            exponent,
    241
    0
                            |b, e| pow_decimal_float(b, *scale, e),
    242
    0
                        *precision,
    243
    0
                        *scale,
    244
    0
                    )?,
    245
    0
                (DataType::Decimal64(precision, scale), DataType::Int64) =>
    246
    0
                        calculate_binary_decimal_math::<Decimal64Type, Int64Type, Decimal64Type, _>(
    247
    0
                            &base,
    248
    0
                            exponent,
    249
    0
                            |b, e| pow_decimal_int(b, *scale, e),
    250
    0
                        *precision,
    251
    0
                        *scale,
    252
    0
                    )?,
    253
    0
                (DataType::Decimal64(precision, scale), DataType::Float64) =>
    254
    0
                        calculate_binary_decimal_math::<Decimal64Type, Float64Type, Decimal64Type, _>(
    255
    0
                            &base,
    256
    0
                            exponent,
    257
    0
                            |b, e| pow_decimal_float(b, *scale, e),
    258
    0
                        *precision,
    259
    0
                        *scale,
    260
    0
                    )?,
    261
    0
                (DataType::Decimal128(precision, scale), DataType::Int64) =>
    262
    0
                        calculate_binary_decimal_math::<Decimal128Type, Int64Type, Decimal128Type, _>(
    263
    0
                            &base,
    264
    0
                            exponent,
    265
    0
                            |b, e| pow_decimal_int(b, *scale, e),
    266
    0
                        *precision,
    267
    0
                        *scale,
    268
    0
                    )?,
    269
    0
                (DataType::Decimal128(precision, scale), DataType::Float64) =>
    270
    0
                        calculate_binary_decimal_math::<
    271
    0
                            Decimal128Type,
    272
    0
                            Float64Type,
    273
    0
                            Decimal128Type,
    274
    0
                            _,
    275
    0
                        >(&base, exponent, |b, e|
    276
    0
                            pow_decimal_float(b, *scale, e),
    277
    0
                        *precision,
    278
    0
                        *scale,
    279
    0
                    )?,
    280
    0
                (DataType::Decimal256(precision, scale),DataType::Int64) =>
    281
    0
                        calculate_binary_decimal_math::<Decimal256Type, Int64Type, Decimal256Type, _>(
    282
    0
                            &base,
    283
    0
                            exponent,
    284
    0
                            |b, e| pow_decimal_int(b, *scale, e),
    285
    0
                        *precision,
    286
    0
                        *scale,
    287
    0
                    )?,
    288
    0
                    (DataType::Decimal256(precision, scale), DataType::Float64) =>
    289
    0
                        calculate_binary_decimal_math::<
    290
    0
                            Decimal256Type,
    291
    0
                            Float64Type,
    292
    0
                            Decimal256Type,
    293
    0
                            _,
    294
    0
                        >(&base, exponent, |b, e|
    295
    0
                            pow_decimal_float(b, *scale, e) ,
    296
    0
                          *precision,
    297
    0
                        *scale,
    298
    0
                    )?,
    299
    0
                (base_type, exp_type) => {
    300
    0
                    return exec_err!(
    301
    0
                        "Unsupported data types for base {base_type:?} and exponent {exp_type:?} for function {}",
    302
    0
                        self.name()
    303
                    )
    304
                }
    305
            };
    306
    0
            Ok(ColumnarValue::Array(arr))
    307
    0
        }
    308
    309
        /// Simplify the `power` function by the relevant rules:
    310
        /// 1. Power(a, 0) ===> 0
    311
        /// 2. Power(a, 1) ===> a
    312
        /// 3. Power(a, Log(a, b)) ===> b
    313
    0
        fn simplify(
    314
    0
            &self,
    315
    0
            mut args: Vec<Expr>,
    316
    0
            info: &dyn SimplifyInfo,
    317
    0
        ) -> Result<ExprSimplifyResult> {
    318
    0
            let exponent = args.pop().ok_or_else(|| {
    319
    0
                plan_datafusion_err!("Expected power to have 2 arguments, got 0")
    320
    0
            })?;
    321
    0
            let base = args.pop().ok_or_else(|| {
    322
    0
                plan_datafusion_err!("Expected power to have 2 arguments, got 1")
    323
    0
            })?;
    324
    325
    0
            let exponent_type = info.get_data_type(&exponent)?;
    326
    0
            match exponent {
    327
    0
                Expr::Literal(value, _)
    328
    0
                    if value == ScalarValue::new_zero(&exponent_type)? =>
    329
                {
    330
                    Ok(ExprSimplifyResult::Simplified(Expr::Literal(
    331
    0
                        ScalarValue::new_one(&info.get_data_type(&base)?)?,
    332
    0
                        None,
    333
                    )))
    334
                }
    335
    0
                Expr::Literal(value, _) if value == ScalarValue::new_one(&exponent_type)? => {
    336
    0
                    Ok(ExprSimplifyResult::Simplified(base))
    337
                }
    338
    0
                Expr::ScalarFunction(ScalarFunction { func, mut args })
    339
    0
                    if is_log(&func) && args.len() == 2 && base == args[0] =>
    340
                {
    341
    0
                    let b = args.pop().unwrap(); // length checked above
    342
    0
                    Ok(ExprSimplifyResult::Simplified(b))
    343
                }
    344
    0
                _ => Ok(ExprSimplifyResult::Original(vec![base, exponent])),
    345
            }
    346
    0
        }
    347
    348
    0
        fn documentation(&self) -> Option<&Documentation> {
    349
    0
            self.doc()
    350
    0
        }
    351
    }
    352
    353
    /// Return true if this function call is a call to `Log`
    354
    0
    fn is_log(func: &ScalarUDF) -> bool {
    355
    0
        func.inner().as_any().downcast_ref::<LogFunc>().is_some()
    356
    0
    }
    357
    358
    #[cfg(test)]
    359
    mod tests {
    360
        use super::*;
    361
        use arrow::array::{Array, Decimal128Array, Float64Array, Int64Array};
    362
        use arrow::datatypes::{Field, DECIMAL128_MAX_SCALE};
    363
        use arrow_buffer::NullBuffer;
    364
        use datafusion_common::cast::{
    365
            as_decimal128_array, as_float64_array, as_int64_array,
    366
        };
    367
        use datafusion_common::config::ConfigOptions;
    368
        use std::sync::Arc;
    369
    370
        #[cfg(test)]
    371
        #[ctor::ctor]
    372
        fn init() {
    373
            // Enable RUST_LOG logging configuration for test
    374
            let _ = env_logger::try_init();
    375
        }
    376
    377
        #[test]
    378
        fn test_power_f64() {
    379
            let arg_fields = vec![
    380
                Field::new("a", DataType::Float64, true).into(),
    381
                Field::new("a", DataType::Float64, true).into(),
    382
            ];
    383
            let args = ScalarFunctionArgs {
    384
                args: vec![
    385
                    ColumnarValue::Array(Arc::new(Float64Array::from(vec![
    386
                        2.0, 2.0, 3.0, 5.0,
    387
                    ]))), // base
    388
                    ColumnarValue::Array(Arc::new(Float64Array::from(vec![
    389
                        3.0, 2.0, 4.0, 4.0,
    390
                    ]))), // exponent
    391
                ],
    392
                arg_fields,
    393
                number_rows: 4,
    394
                return_field: Field::new("f", DataType::Float64, true).into(),
    395
                config_options: Arc::new(ConfigOptions::default()),
    396
            };
    397
            let result = PowerFunc::new()
    398
                .invoke_with_args(args)
    399
                .expect("failed to initialize function power");
    400
    401
            match result {
    402
                ColumnarValue::Array(arr) => {
    403
                    let floats = as_float64_array(&arr)
    404
                        .expect("failed to convert result to a Float64Array");
    405
                    assert_eq!(floats.len(), 4);
    406
                    assert_eq!(floats.value(0), 8.0);
    407
                    assert_eq!(floats.value(1), 4.0);
    408
                    assert_eq!(floats.value(2), 81.0);
    409
                    assert_eq!(floats.value(3), 625.0);
    410
                }
    411
                ColumnarValue::Scalar(_) => {
    412
                    panic!("Expected an array value")
    413
                }
    414
            }
    415
        }
    416
    417
        #[test]
    418
        fn test_power_i64() {
    419
            let arg_fields = vec![
    420
                Field::new("a", DataType::Int64, true).into(),
    421
                Field::new("a", DataType::Int64, true).into(),
    422
            ];
    423
            let args = ScalarFunctionArgs {
    424
                args: vec![
    425
                    ColumnarValue::Array(Arc::new(Int64Array::from(vec![2, 2, 3, 5]))), // base
    426
                    ColumnarValue::Array(Arc::new(Int64Array::from(vec![3, 2, 4, 4]))), // exponent
    427
                ],
    428
                arg_fields,
    429
                number_rows: 4,
    430
                return_field: Field::new("f", DataType::Int64, true).into(),
    431
                config_options: Arc::new(ConfigOptions::default()),
    432
            };
    433
            let result = PowerFunc::new()
    434
                .invoke_with_args(args)
    435
                .expect("failed to initialize function power");
    436
    437
            match result {
    438
                ColumnarValue::Array(arr) => {
    439
                    let ints = as_int64_array(&arr)
    440
                        .expect("failed to convert result to a Int64Array");
    441
    442
                    assert_eq!(ints.len(), 4);
    443
                    assert_eq!(ints.value(0), 8);
    444
                    assert_eq!(ints.value(1), 4);
    445
                    assert_eq!(ints.value(2), 81);
    446
                    assert_eq!(ints.value(3), 625);
    447
                }
    448
                ColumnarValue::Scalar(_) => {
    449
                    panic!("Expected an array value")
    450
                }
    451
            }
    452
        }
    453
    454
        #[test]
    455
        fn test_power_i128() {
    456
            let arg_fields = vec![
    457
                Field::new(
    458
                    "a",
    459
                    DataType::Decimal128(DECIMAL128_MAX_SCALE as u8, 0),
    460
                    true,
    461
                )
    462
                .into(),
    463
                Field::new("a", DataType::Int64, true).into(),
    464
            ];
    465
            let args = ScalarFunctionArgs {
    466
                args: vec![
    467
                    ColumnarValue::Array(Arc::new(
    468
                        Decimal128Array::from(vec![2, 2, 3, 5, 0, 5])
    469
                            .with_precision_and_scale(DECIMAL128_MAX_SCALE as u8, 0)
    470
                            .unwrap(),
    471
                    )), // base
    472
                    ColumnarValue::Array(Arc::new(Int64Array::from(vec![3, 2, 4, 4, 4, 0]))), // exponent
    473
                ],
    474
                arg_fields,
    475
                number_rows: 6,
    476
                return_field: Field::new(
    477
                    "f",
    478
                    DataType::Decimal128(DECIMAL128_MAX_SCALE as u8, 0),
    479
                    true,
    480
                )
    481
                .into(),
    482
                config_options: Arc::new(ConfigOptions::default()),
    483
            };
    484
            let result = PowerFunc::new()
    485
                .invoke_with_args(args)
    486
                .expect("failed to initialize function power");
    487
    488
            match result {
    489
                ColumnarValue::Array(arr) => {
    490
                    let ints = as_decimal128_array(&arr)
    491
                        .expect("failed to convert result to an array");
    492
    493
                    assert_eq!(ints.len(), 6);
    494
                    assert_eq!(ints.value(0), i128::from(8));
    495
                    assert_eq!(ints.value(1), i128::from(4));
    496
                    assert_eq!(ints.value(2), i128::from(81));
    497
                    assert_eq!(ints.value(3), i128::from(625));
    498
                    assert_eq!(ints.value(4), i128::from(0));
    499
                    assert_eq!(ints.value(5), i128::from(1));
    500
                }
    501
                ColumnarValue::Scalar(_) => {
    502
                    panic!("Expected an array value")
    503
                }
    504
            }
    505
        }
    506
    507
        #[test]
    508
        fn test_power_array_null() {
    509
            let arg_fields = vec![
    510
                Field::new("a", DataType::Int64, true).into(),
    511
                Field::new("a", DataType::Int64, true).into(),
    512
            ];
    513
            let args = ScalarFunctionArgs {
    514
                args: vec![
    515
                    ColumnarValue::Array(Arc::new(Int64Array::from(vec![2, 2, 2]))), // base
    516
                    ColumnarValue::Array(Arc::new(Int64Array::from_iter_values_with_nulls(
    517
                        vec![1, 2, 3],
    518
                        Some(NullBuffer::from(vec![true, false, true])),
    519
                    ))), // exponent
    520
                ],
    521
                arg_fields,
    522
                number_rows: 1,
    523
                return_field: Field::new("f", DataType::Int64, true).into(),
    524
                config_options: Arc::new(ConfigOptions::default()),
    525
            };
    526
            let result = PowerFunc::new()
    527
                .invoke_with_args(args)
    528
                .expect("failed to initialize function power");
    529
    530
            match result {
    531
                ColumnarValue::Array(arr) => {
    532
                    let ints =
    533
                        as_int64_array(&arr).expect("failed to convert result to an array");
    534
    535
                    assert_eq!(ints.len(), 3);
    536
                    assert!(!ints.is_null(0));
    537
                    assert_eq!(ints.value(0), i64::from(2));
    538
                    assert!(ints.is_null(1));
    539
                    assert!(!ints.is_null(2));
    540
                    assert_eq!(ints.value(2), i64::from(8));
    541
                }
    542
                ColumnarValue::Scalar(_) => {
    543
                    panic!("Expected an array value")
    544
                }
    545
            }
    546
        }
    547
    548
        #[test]
    549
        fn test_power_decimal_with_scale() {
    550
            // 2.5 ^ 4 = 39
    551
            // 2.5 is 25 in Decimal128(2, 1) by parsing rules
    552
            // Signature is Decimal128(2, 1) -> Int64 -> Decimal128(2, 1), therefore
    553
            // result is 390 in Decimal128(2, 1) aka 39 in unscaled Decimal128(2, 0)
    554
            let arg_fields = vec![
    555
                Field::new(
    556
                    "a",
    557
                    DataType::Decimal128(DECIMAL128_MAX_SCALE as u8, 0),
    558
                    true,
    559
                )
    560
                .into(),
    561
                Field::new("a", DataType::Int64, true).into(),
    562
            ];
    563
            let args = ScalarFunctionArgs {
    564
                args: vec![
    565
                    ColumnarValue::Scalar(ScalarValue::Decimal128(
    566
                        Some(i128::from(25)),
    567
                        2,
    568
                        1,
    569
                    )), // base
    570
                    ColumnarValue::Scalar(ScalarValue::Int64(Some(4))), // exponent
    571
                ],
    572
                arg_fields,
    573
                number_rows: 1,
    574
                return_field: Field::new("f", DataType::Decimal128(2, 1), true).into(),
    575
                config_options: Arc::new(ConfigOptions::default()),
    576
            };
    577
            let result = PowerFunc::new()
    578
                .invoke_with_args(args)
    579
                .expect("failed to initialize function power");
    580
    581
            match result {
    582
                ColumnarValue::Array(arr) => {
    583
                    let ints = as_decimal128_array(&arr)
    584
                        .expect("failed to convert result to an array");
    585
    586
                    assert_eq!(ints.len(), 1);
    587
                    assert_eq!(ints.value(0), i128::from(390));
    588
                    // Signature stays the same as input
    589
                    assert_eq!(*arr.data_type(), DataType::Decimal128(2, 1));
    590
                }
    591
                ColumnarValue::Scalar(_) => {
    592
                    panic!("Expected an array value")
    593
                }
    594
            }
    595
        }
    596
    597
        #[test]
    598
        fn test_pow_decimal128_helper() {
    599
            // Expression: 2.5 ^ 4 = 39.0625
    600
            assert_eq!(pow_decimal_int(25, 1, 4).unwrap(), i128::from(390));
    601
            assert_eq!(pow_decimal_int(2500, 3, 4).unwrap(), i128::from(39062));
    602
            assert_eq!(pow_decimal_int(25000, 4, 4).unwrap(), i128::from(390625));
    603
    604
            // Expression: 25 ^ 4 = 390625
    605
            assert_eq!(pow_decimal_int(25, 0, 4).unwrap(), i128::from(390625));
    606
    607
            // Expressions for edge cases
    608
            assert_eq!(pow_decimal_int(25, 1, 1).unwrap(), i128::from(25));
    609
            assert_eq!(pow_decimal_int(25, 0, 1).unwrap(), i128::from(25));
    610
            assert_eq!(pow_decimal_int(25, 0, 0).unwrap(), i128::from(1));
    611
            assert_eq!(pow_decimal_int(25, 1, 0).unwrap(), i128::from(10));
    612
    613
            assert_eq!(
    614
                pow_decimal_int(25, -1, 4).unwrap_err().to_string(),
    615
                "Not yet implemented: Negative scale is not yet supported value: -1"
    616
            );
    617
        }
    618
    }