diff --git a/dashboard/proto/gen/expr.ts b/dashboard/proto/gen/expr.ts index fe95c1bc0109..039adaa0eeda 100644 --- a/dashboard/proto/gen/expr.ts +++ b/dashboard/proto/gen/expr.ts @@ -106,6 +106,7 @@ export const ExprNode_Type = { OVERLAY: "OVERLAY", REGEXP_MATCH: "REGEXP_MATCH", POW: "POW", + EXP: "EXP", /** IS_TRUE - Boolean comparison */ IS_TRUE: "IS_TRUE", IS_NOT_TRUE: "IS_NOT_TRUE", @@ -343,6 +344,9 @@ export function exprNode_TypeFromJSON(object: any): ExprNode_Type { case 233: case "POW": return ExprNode_Type.POW; + case 234: + case "EXP": + return ExprNode_Type.EXP; case 301: case "IS_TRUE": return ExprNode_Type.IS_TRUE; @@ -544,6 +548,8 @@ export function exprNode_TypeToJSON(object: ExprNode_Type): string { return "REGEXP_MATCH"; case ExprNode_Type.POW: return "POW"; + case ExprNode_Type.EXP: + return "EXP"; 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 index 869b3e936cbd..ac67691dec08 100644 --- a/e2e_test/batch/functions/pow.slt.part +++ b/e2e_test/batch/functions/pow.slt.part @@ -3,6 +3,11 @@ select pow(2.0, 3.0) ---- 8 +query R +select power(2.0, 3.0) +---- +8 + query R select pow(2.0::decimal, 3.0::decimal) ---- @@ -49,3 +54,38 @@ select pow(100000, 200000000000000); statement error QueryError: Expr error: Numeric out of range select pow(-100000, 200000000000001); + +query R +select exp(0::smallint); +---- +1 + +query R +select exp(0.0); +---- +1 + +query R +select exp(0.0::decimal); +---- +1 + +query R +select exp(2.0); +---- +7.38905609893065 + +query R +select exp(2::smallint) +---- +7.38905609893065 + +statement error QueryError: Expr error: Numeric out of range +select exp(10000000); + +# We remark that this test case underflows in PG. TODO: We can make it fully compatible if necessary +# https://github.com/postgres/postgres/blob/REL_15_2/src/backend/utils/adt/float.c#L1649 +query T +select exp(-10000000); +---- +0 \ No newline at end of file diff --git a/proto/expr.proto b/proto/expr.proto index f752672f2605..519a4835aa1b 100644 --- a/proto/expr.proto +++ b/proto/expr.proto @@ -94,6 +94,7 @@ message ExprNode { OVERLAY = 231; REGEXP_MATCH = 232; POW = 233; + EXP = 234; // Boolean comparison IS_TRUE = 301; diff --git a/src/expr/src/expr/build_expr_from_prost.rs b/src/expr/src/expr/build_expr_from_prost.rs index 5708f4ce690c..5273cf1a7656 100644 --- a/src/expr/src/expr/build_expr_from_prost.rs +++ b/src/expr/src/expr/build_expr_from_prost.rs @@ -62,7 +62,7 @@ pub fn build_from_prost(prost: &ExprNode) -> Result { match prost.get_expr_type().unwrap() { // Fixed number of arguments and based on `Unary/Binary/Ternary/...Expression` Cast | Upper | Lower | Md5 | Not | IsTrue | IsNotTrue | IsFalse | IsNotFalse | IsNull - | IsNotNull | Neg | Ascii | Abs | Ceil | Floor | Round | BitwiseNot | CharLength + | IsNotNull | Neg | Ascii | Abs | Ceil | Floor | Round | Exp | BitwiseNot | CharLength | BoolOut | OctetLength | BitLength | ToTimestamp => build_unary_expr_prost(prost), Equal | NotEqual | LessThan | LessThanOrEqual | GreaterThan | GreaterThanOrEqual | Add | Subtract | Multiply | Divide | Modulus | Extract | RoundDigit | Pow | TumbleStart diff --git a/src/expr/src/expr/expr_binary_nonnull.rs b/src/expr/src/expr/expr_binary_nonnull.rs index 1497d033785c..910b563af67f 100644 --- a/src/expr/src/expr/expr_binary_nonnull.rs +++ b/src/expr/src/expr/expr_binary_nonnull.rs @@ -13,8 +13,8 @@ // limitations under the License. use risingwave_common::array::{ - Array, BoolArray, DecimalArray, I32Array, I64Array, IntervalArray, ListArray, NaiveDateArray, - NaiveDateTimeArray, StructArray, Utf8Array, + Array, BoolArray, DecimalArray, F64Array, I32Array, I64Array, IntervalArray, ListArray, + NaiveDateArray, NaiveDateTimeArray, StructArray, Utf8Array, }; use risingwave_common::types::*; use risingwave_pb::expr::expr_node::Type; @@ -288,56 +288,6 @@ 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) @@ -713,13 +663,9 @@ pub fn new_binary_expr( }, } } - Type::Pow => { - gen_binary_expr_pow! { - gen_atm_impl, - l, r, ret, - general_pow, - } - } + Type::Pow => Box::new(BinaryExpression::::new( + l, r, ret, pow_f64, + )), 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/expr_unary.rs b/src/expr/src/expr/expr_unary.rs index fc7853f55fd8..00f9151078a0 100644 --- a/src/expr/src/expr/expr_unary.rs +++ b/src/expr/src/expr/expr_unary.rs @@ -29,6 +29,7 @@ use crate::vector_op::bitwise_op::general_bitnot; use crate::vector_op::cast::*; use crate::vector_op::cmp::{is_false, is_not_false, is_not_true, is_true}; use crate::vector_op::conjunction; +use crate::vector_op::exp::exp_f64; use crate::vector_op::length::{bit_length, length_default, octet_length}; use crate::vector_op::lower::lower; use crate::vector_op::ltrim::ltrim; @@ -288,12 +289,17 @@ pub fn new_unary_expr( (ProstType::Ceil, _, _) => { gen_round_expr! {"Ceil", child_expr, return_type, ceil_f64, ceil_decimal} } - (ProstType::Floor, _, _) => { + (ProstType::Floor, DataType::Float64, DataType::Float64) => { gen_round_expr! {"Floor", child_expr, return_type, floor_f64, floor_decimal} } (ProstType::Round, _, _) => { gen_round_expr! {"Ceil", child_expr, return_type, round_f64, round_decimal} } + (ProstType::Exp, _, _) => Box::new(UnaryExpression::::new( + child_expr, + return_type, + exp_f64, + )), (ProstType::ToTimestamp, DataType::Timestamptz, DataType::Float64) => { Box::new(UnaryExpression::::new( child_expr, diff --git a/src/expr/src/sig/func.rs b/src/expr/src/sig/func.rs index d557596f2a8e..aaf47d1ca366 100644 --- a/src/expr/src/sig/func.rs +++ b/src/expr/src/sig/func.rs @@ -173,6 +173,7 @@ fn build_type_derive_map() -> FuncSigMap { ); map.insert(E::RoundDigit, vec![T::Decimal, T::Int32], T::Decimal); map.insert(E::Pow, vec![T::Float64, T::Float64], T::Float64); + map.insert(E::Exp, vec![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 7db4856ed4ae..0b5218579843 100644 --- a/src/expr/src/vector_op/arithmetic_op.rs +++ b/src/expr/src/vector_op/arithmetic_op.rs @@ -16,7 +16,8 @@ use std::convert::TryInto; use std::fmt::Debug; use chrono::{Duration, NaiveDateTime}; -use num_traits::{CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, Pow, Signed, Zero}; +use num_traits::real::Real; +use num_traits::{CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, Signed, Zero}; use risingwave_common::types::{ CheckedAdd, Decimal, IntervalUnit, NaiveDateTimeWrapper, NaiveDateWrapper, NaiveTimeWrapper, OrderedF64, @@ -108,14 +109,8 @@ 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()); +pub fn pow_f64(l: OrderedF64, r: OrderedF64) -> Result { + let res = l.powf(r); if res.is_infinite() { Err(ExprError::NumericOutOfRange) } else { diff --git a/src/expr/src/vector_op/exp.rs b/src/expr/src/vector_op/exp.rs new file mode 100644 index 000000000000..bd52273612a0 --- /dev/null +++ b/src/expr/src/vector_op/exp.rs @@ -0,0 +1,27 @@ +// Copyright 2023 RisingWave Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use num_traits::Float; +use risingwave_common::types::OrderedF64; + +use crate::{ExprError, Result}; + +pub fn exp_f64(input: OrderedF64) -> Result { + let res = input.exp(); + if res.is_infinite() { + Err(ExprError::NumericOutOfRange) + } else { + Ok(res) + } +} diff --git a/src/expr/src/vector_op/mod.rs b/src/expr/src/vector_op/mod.rs index 6de7e2d73f2d..d3f07ad7fed9 100644 --- a/src/expr/src/vector_op/mod.rs +++ b/src/expr/src/vector_op/mod.rs @@ -22,6 +22,7 @@ pub mod cmp; pub mod concat_op; pub mod conjunction; pub mod date_trunc; +pub mod exp; pub mod extract; pub mod format_type; pub mod length; diff --git a/src/frontend/src/binder/expr/function.rs b/src/frontend/src/binder/expr/function.rs index e8afb1cd01c3..5a3a1ba48af6 100644 --- a/src/frontend/src/binder/expr/function.rs +++ b/src/frontend/src/binder/expr/function.rs @@ -328,9 +328,12 @@ impl Binder { ]), ), ("pow", raw_call(ExprType::Pow)), + // "power" is the function name used in PG. + ("power", raw_call(ExprType::Pow)), ("ceil", raw_call(ExprType::Ceil)), ("floor", raw_call(ExprType::Floor)), ("abs", raw_call(ExprType::Abs)), + ("exp", raw_call(ExprType::Exp)), ("mod", raw_call(ExprType::Modulus)), ( "to_timestamp", diff --git a/src/tests/regress/data/expected/float8.out b/src/tests/regress/data/expected/float8.out index adcfc2c1fbf2..ccefc5798c4a 100644 --- a/src/tests/regress/data/expected/float8.out +++ b/src/tests/regress/data/expected/float8.out @@ -616,7 +616,15 @@ ERROR: cannot take logarithm of zero SELECT ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; ERROR: cannot take logarithm of a negative number SELECT exp(f.f1) from FLOAT8_TBL f; -ERROR: value out of range: underflow + exp +----------------------- + 1 + 0 + 0 + 7.399123060905129e-16 + 1 +(5 rows) + SELECT f.f1 / '0.0' from FLOAT8_TBL f; ERROR: division by zero SELECT * FROM FLOAT8_TBL; diff --git a/src/tests/regress/data/sql/float8.sql b/src/tests/regress/data/sql/float8.sql index d66d57878fb1..6448a8e2eb0a 100644 --- a/src/tests/regress/data/sql/float8.sql +++ b/src/tests/regress/data/sql/float8.sql @@ -182,7 +182,7 @@ SELECT ln(f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ; SELECT ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; -SELECT exp(f.f1) from FLOAT8_TBL f; +--@ SELECT exp(f.f1) from FLOAT8_TBL f; --@ SELECT f.f1 / '0.0' from FLOAT8_TBL f;