diff --git a/dashboard/proto/gen/expr.ts b/dashboard/proto/gen/expr.ts index 8ea49251de5db..930440fc3152a 100644 --- a/dashboard/proto/gen/expr.ts +++ b/dashboard/proto/gen/expr.ts @@ -105,6 +105,7 @@ export const ExprNode_Type = { BIT_LENGTH: "BIT_LENGTH", OVERLAY: "OVERLAY", REGEXP_MATCH: "REGEXP_MATCH", + POW: "POW", /** IS_TRUE - Boolean comparison */ IS_TRUE: "IS_TRUE", IS_NOT_TRUE: "IS_NOT_TRUE", @@ -338,6 +339,9 @@ export function exprNode_TypeFromJSON(object: any): ExprNode_Type { case 232: case "REGEXP_MATCH": return ExprNode_Type.REGEXP_MATCH; + case 233: + case "POW": + return ExprNode_Type.POW; case 301: case "IS_TRUE": return ExprNode_Type.IS_TRUE; @@ -534,6 +538,8 @@ export function exprNode_TypeToJSON(object: ExprNode_Type): string { return "OVERLAY"; case ExprNode_Type.REGEXP_MATCH: return "REGEXP_MATCH"; + case ExprNode_Type.POW: + return "POW"; case ExprNode_Type.IS_TRUE: return "IS_TRUE"; case ExprNode_Type.IS_NOT_TRUE: diff --git a/e2e_test/batch/functions/pow.slt.part b/e2e_test/batch/functions/pow.slt.part new file mode 100644 index 0000000000000..869b3e936cbd0 --- /dev/null +++ b/e2e_test/batch/functions/pow.slt.part @@ -0,0 +1,51 @@ +query R +select pow(2.0, 3.0) +---- +8 + +query R +select pow(2.0::decimal, 3.0::decimal) +---- +8 + +query R +select pow(2.0::double, 3.0::double) +---- +8 + +query R +select pow(2.0::smallint, 3.0::smallint) +---- +8 + +query R +select pow(2.0::bigint, 3.0::bigint) +---- +8 + +query R +select pow(2.0, -2); +---- +0.25 + +query R +select pow(2.23, -2.33); +---- +0.15432975583772085 + +query R +select pow(100000, 0); +---- +1 + +query R +select pow(100000, -200000000000000); +---- +0 + +statement error QueryError: Expr error: Numeric out of range +select pow(100000, 200000000000000); + + +statement error QueryError: Expr error: Numeric out of range +select pow(-100000, 200000000000001); diff --git a/proto/expr.proto b/proto/expr.proto index e59a1c3e75da9..3893fe6df2ae2 100644 --- a/proto/expr.proto +++ b/proto/expr.proto @@ -93,6 +93,7 @@ message ExprNode { BIT_LENGTH = 230; OVERLAY = 231; REGEXP_MATCH = 232; + POW = 233; // Boolean comparison IS_TRUE = 301; diff --git a/src/common/src/types/ordered_float.rs b/src/common/src/types/ordered_float.rs index 9eb6705e2bc1a..20e0412e23ff1 100644 --- a/src/common/src/types/ordered_float.rs +++ b/src/common/src/types/ordered_float.rs @@ -54,7 +54,7 @@ use core::str::FromStr; pub use num_traits::Float; use num_traits::{ AsPrimitive, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, - FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Zero, + FromPrimitive, Num, NumCast, One, Pow, Signed, ToPrimitive, Zero, }; // masks for the parts of the IEEE 754 float @@ -389,6 +389,17 @@ impl_ordered_float_binop! {Mul, mul, MulAssign, mul_assign} impl_ordered_float_binop! {Div, div, DivAssign, div_assign} impl_ordered_float_binop! {Rem, rem, RemAssign, rem_assign} +impl Pow> for OrderedFloat +where + T: Float, +{ + type Output = OrderedFloat; + + fn pow(self, rhs: Self) -> Self::Output { + OrderedFloat(self.0.powf(rhs.0)) + } +} + impl CheckedAdd for OrderedFloat where T: Float, diff --git a/src/expr/src/expr/expr_binary_nonnull.rs b/src/expr/src/expr/expr_binary_nonnull.rs index 5f9a8d84cd724..38e72bfa70146 100644 --- a/src/expr/src/expr/expr_binary_nonnull.rs +++ b/src/expr/src/expr/expr_binary_nonnull.rs @@ -288,6 +288,56 @@ macro_rules! gen_binary_expr_atm { }; } +/// `gen_binary_expr_atm` is similar to `gen_binary_expr_cmp`. +/// `atm` means arithmetic here. +/// They are differentiate cuz one type may not support atm and cmp at the same time. For example, +/// Varchar can support compare but not arithmetic. +/// * `$general_f`: generic atm function (require a common ``TryInto`` type for two input) +/// * `$i1`, `$i2`, `$rt`, `$func`: extra list passed to `$macro` directly +macro_rules! gen_binary_expr_pow { + ($macro:ident, $l:expr, $r:expr, $ret:expr, $general_f:ident,) => { + $macro! { + [$l, $r, $ret], + { int16, int16, float64, $general_f }, + { int16, int32, float64, $general_f }, + { int16, int64, float64, $general_f }, + { int16, float32, float64, $general_f }, + { int16, float64, float64, $general_f }, + { int32, int16, float64, $general_f }, + { int32, int32, float64, $general_f }, + { int32, int64, float64, $general_f }, + { int32, float32, float64, $general_f }, + { int32, float64, float64, $general_f }, + { int64, int16, float64, $general_f }, + { int64, int32, float64, $general_f }, + { int64, int64, float64, $general_f }, + { int64, float32, float64 , $general_f}, + { int64, float64, float64, $general_f }, + { float32, int16, float64, $general_f }, + { float32, int32, float64, $general_f }, + { float32, int64, float64 , $general_f}, + { float32, float32, float64, $general_f }, + { float32, float64, float64, $general_f }, + { float64, int16, float64, $general_f }, + { float64, int32, float64, $general_f }, + { float64, int64, float64, $general_f }, + { float64, float32, float64, $general_f }, + { float64, float64, float64, $general_f }, + { decimal, int16, float64, $general_f }, + { decimal, int32, float64, $general_f }, + { decimal, int64, float64, $general_f }, + { decimal, float32, float64, $general_f }, + { decimal, float64, float64, $general_f }, + { int16, decimal, float64, $general_f }, + { int32, decimal, float64, $general_f }, + { int64, decimal, float64, $general_f }, + { decimal, decimal, float64, $general_f }, + { float32, decimal, float64, $general_f }, + { float64, decimal, float64, $general_f }, + } + }; +} + /// `gen_binary_expr_bitwise` is similar to `gen_binary_expr_atm`. /// They are differentiate because bitwise operation only supports integral datatype. /// * `$general_f`: generic atm function (require a common ``TryInto`` type for two input) @@ -662,6 +712,13 @@ pub fn new_binary_expr( }, } } + Type::Pow => { + gen_binary_expr_pow! { + gen_atm_impl, + l, r, ret, + general_pow, + } + } Type::Extract => build_extract_expr(ret, l, r)?, Type::AtTimeZone => build_at_time_zone_expr(ret, l, r)?, Type::CastWithTimeZone => build_cast_with_time_zone_expr(ret, l, r)?, diff --git a/src/expr/src/expr/mod.rs b/src/expr/src/expr/mod.rs index 1754a700edf14..db51fe543e05f 100644 --- a/src/expr/src/expr/mod.rs +++ b/src/expr/src/expr/mod.rs @@ -110,7 +110,7 @@ pub fn build_from_prost(prost: &ExprNode) -> Result { | IsNotNull | Neg | Ascii | Abs | Ceil | Floor | Round | BitwiseNot | CharLength | BoolOut | OctetLength | BitLength | ToTimestamp => build_unary_expr_prost(prost), Equal | NotEqual | LessThan | LessThanOrEqual | GreaterThan | GreaterThanOrEqual | Add - | Subtract | Multiply | Divide | Modulus | Extract | RoundDigit | TumbleStart + | Subtract | Multiply | Divide | Modulus | Extract | RoundDigit | Pow | TumbleStart | Position | BitwiseShiftLeft | BitwiseShiftRight | BitwiseAnd | BitwiseOr | BitwiseXor | ConcatOp | AtTimeZone | CastWithTimeZone => build_binary_expr_prost(prost), And | Or | IsDistinctFrom | IsNotDistinctFrom | ArrayAccess => { diff --git a/src/expr/src/sig/func.rs b/src/expr/src/sig/func.rs index af5736ddf15a7..6ed361d126d1c 100644 --- a/src/expr/src/sig/func.rs +++ b/src/expr/src/sig/func.rs @@ -172,6 +172,7 @@ fn build_type_derive_map() -> FuncSigMap { &[T::Int16, T::Int32, T::Int64, T::Decimal], ); map.insert(E::RoundDigit, vec![T::Decimal, T::Int32], T::Decimal); + map.insert(E::Pow, vec![T::Float64, T::Float64], T::Float64); // build bitwise operator // bitwise operator diff --git a/src/expr/src/vector_op/arithmetic_op.rs b/src/expr/src/vector_op/arithmetic_op.rs index d14d5677f309b..7db4856ed4aee 100644 --- a/src/expr/src/vector_op/arithmetic_op.rs +++ b/src/expr/src/vector_op/arithmetic_op.rs @@ -16,7 +16,7 @@ use std::convert::TryInto; use std::fmt::Debug; use chrono::{Duration, NaiveDateTime}; -use num_traits::{CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, Signed, Zero}; +use num_traits::{CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, Pow, Signed, Zero}; use risingwave_common::types::{ CheckedAdd, Decimal, IntervalUnit, NaiveDateTimeWrapper, NaiveDateWrapper, NaiveTimeWrapper, OrderedF64, @@ -108,6 +108,21 @@ pub fn decimal_abs(decimal: Decimal) -> Result { Ok(Decimal::abs(&decimal)) } +#[inline(always)] +pub fn general_pow(l: T1, r: T2) -> Result +where + T1: Into + Debug, + T2: Into + Debug, + T3: Pow + num_traits::Float, +{ + let res = l.into().powf(r.into()); + if res.is_infinite() { + Err(ExprError::NumericOutOfRange) + } else { + Ok(res) + } +} + #[inline(always)] pub fn general_atm(l: T1, r: T2, atm: F) -> Result where diff --git a/src/frontend/src/binder/expr/function.rs b/src/frontend/src/binder/expr/function.rs index 1f3e219415c4b..f7819d144869e 100644 --- a/src/frontend/src/binder/expr/function.rs +++ b/src/frontend/src/binder/expr/function.rs @@ -327,6 +327,7 @@ impl Binder { (1, raw_call(ExprType::Round)), ]), ), + ("pow", raw_call(ExprType::Pow)), ("ceil", raw_call(ExprType::Ceil)), ("floor", raw_call(ExprType::Floor)), ("abs", raw_call(ExprType::Abs)), diff --git a/src/tests/sqlsmith/src/sql_gen/expr.rs b/src/tests/sqlsmith/src/sql_gen/expr.rs index 73c882f28065d..ad99652c49f72 100644 --- a/src/tests/sqlsmith/src/sql_gen/expr.rs +++ b/src/tests/sqlsmith/src/sql_gen/expr.rs @@ -521,6 +521,7 @@ fn make_general_expr(func: ExprType, exprs: Vec) -> Option { E::IsNotFalse => Some(Expr::IsNotFalse(Box::new(exprs[0].clone()))), E::Position => Some(Expr::Function(make_simple_func("position", &exprs))), E::RoundDigit => Some(Expr::Function(make_simple_func("round", &exprs))), + E::Pow => Some(Expr::Function(make_simple_func("pow", &exprs))), E::Repeat => Some(Expr::Function(make_simple_func("repeat", &exprs))), E::CharLength => Some(Expr::Function(make_simple_func("char_length", &exprs))), E::Substr => Some(Expr::Function(make_simple_func("substr", &exprs))),