datafusion_functions/math/
power.rs1use super::log::LogFunc;
20
21use crate::utils::calculate_binary_math;
22use arrow::array::{Array, ArrayRef};
23use arrow::datatypes::{DataType, Float64Type};
24use arrow::error::ArrowError;
25use datafusion_common::types::{NativeType, logical_float64};
26use datafusion_common::utils::take_function_args;
27use datafusion_common::{Result, ScalarValue, internal_err};
28use datafusion_expr::expr::ScalarFunction;
29use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyContext};
30use datafusion_expr::{
31 Cast, Coercion, ColumnarValue, Documentation, Expr, ScalarFunctionArgs, ScalarUDF,
32 ScalarUDFImpl, Signature, TypeSignatureClass, Volatility, lit,
33};
34use datafusion_macros::user_doc;
35
36#[inline]
38fn float64_power_checked(base: f64, exp: f64) -> Result<f64, ArrowError> {
39 if base == 0.0 && exp < 0.0 {
40 return Err(ArrowError::ComputeError(
41 "zero raised to a negative power is undefined".to_string(),
42 ));
43 }
44 Ok(base.powf(exp))
45}
46
47#[user_doc(
48 doc_section(label = "Math Functions"),
49 description = "Returns a base expression raised to the power of an exponent.",
50 syntax_example = "power(base, exponent)",
51 sql_example = r#"```sql
52> SELECT power(2, 3);
53+-------------+
54| power(2,3) |
55+-------------+
56| 8 |
57+-------------+
58```"#,
59 standard_argument(name = "base", prefix = "Numeric"),
60 standard_argument(name = "exponent", prefix = "Exponent numeric")
61)]
62#[derive(Debug, PartialEq, Eq, Hash)]
63pub struct PowerFunc {
64 signature: Signature,
65 aliases: Vec<String>,
66}
67
68impl Default for PowerFunc {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74impl PowerFunc {
75 pub fn new() -> Self {
76 let float = Coercion::new_implicit(
77 TypeSignatureClass::Native(logical_float64()),
78 vec![TypeSignatureClass::Numeric],
79 NativeType::Float64,
80 );
81 Self {
82 signature: Signature::coercible(vec![float; 2], Volatility::Immutable),
83 aliases: vec![String::from("pow")],
84 }
85 }
86}
87
88impl ScalarUDFImpl for PowerFunc {
89 fn name(&self) -> &str {
90 "power"
91 }
92
93 fn signature(&self) -> &Signature {
94 &self.signature
95 }
96
97 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
98 let [_base, _exponent] = take_function_args(self.name(), arg_types)?;
99 Ok(DataType::Float64)
100 }
101
102 fn is_strict(&self) -> bool {
103 true
104 }
105
106 fn aliases(&self) -> &[String] {
107 &self.aliases
108 }
109
110 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
111 let [base, exponent] = take_function_args(self.name(), &args.args)?;
112 let base = base.to_array(args.number_rows)?;
113
114 let arr: ArrayRef = match (base.data_type(), exponent.data_type()) {
115 (DataType::Float64, DataType::Float64) => {
116 calculate_binary_math::<Float64Type, Float64Type, Float64Type, _>(
117 &base,
118 exponent,
119 float64_power_checked,
120 )?
121 }
122 (base_type, exp_type) => {
123 return internal_err!(
124 "Unsupported data types for base {base_type:?} and exponent {exp_type:?} for power"
125 );
126 }
127 };
128 Ok(ColumnarValue::Array(arr))
129 }
130
131 fn simplify(
136 &self,
137 args: Vec<Expr>,
138 info: &SimplifyContext,
139 ) -> Result<ExprSimplifyResult> {
140 let [base, exponent] = take_function_args("power", args)?;
141 let base_type = info.get_data_type(&base)?;
142 let exponent_type = info.get_data_type(&exponent)?;
143 let base_nullable = info.nullable(&base)?;
144 let return_type =
145 self.return_type(&[base_type.clone(), exponent_type.clone()])?;
146
147 if base_type.is_null() || exponent_type.is_null() {
149 return Ok(ExprSimplifyResult::Simplified(lit(
150 ScalarValue::Null.cast_to(&return_type)?
151 )));
152 }
153
154 let cast_to_return_type = |expr: Expr, expr_type: &DataType| {
162 if expr_type == &return_type {
163 expr
164 } else {
165 Expr::Cast(Cast::new(Box::new(expr), return_type.clone()))
166 }
167 };
168
169 match exponent {
170 Expr::Literal(value, _)
171 if value == ScalarValue::new_zero(&exponent_type)? && !base_nullable =>
172 {
173 Ok(ExprSimplifyResult::Simplified(lit(ScalarValue::new_one(
174 &return_type,
175 )?)))
176 }
177 Expr::Literal(value, _) if value == ScalarValue::new_one(&exponent_type)? => {
178 Ok(ExprSimplifyResult::Simplified(cast_to_return_type(
179 base, &base_type,
180 )))
181 }
182 Expr::ScalarFunction(ScalarFunction { func, mut args })
183 if is_log(&func)
184 && args.len() == 2
185 && base == args[0]
186 && !base_nullable =>
187 {
188 let b = args.pop().unwrap(); let b_type = info.get_data_type(&b)?;
190 Ok(ExprSimplifyResult::Simplified(cast_to_return_type(
191 b, &b_type,
192 )))
193 }
194 _ => Ok(ExprSimplifyResult::Original(vec![base, exponent])),
195 }
196 }
197
198 fn documentation(&self) -> Option<&Documentation> {
199 self.doc()
200 }
201}
202
203fn is_log(func: &ScalarUDF) -> bool {
205 func.inner().is::<LogFunc>()
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_float64_power_checked_zero_negative_exp() {
214 assert_eq!(float64_power_checked(0.0, 1.0).unwrap(), 0.0);
215 assert_eq!(float64_power_checked(2.0, -1.0).unwrap(), 0.5);
216 for base in [0.0f64, -0.0] {
217 assert!(float64_power_checked(base, -1.0).is_err());
218 assert!(float64_power_checked(base, -0.5).is_err());
219 }
220 }
221}