diff --git a/e2e_test/batch/functions/array_max.slt.part b/e2e_test/batch/functions/array_max.slt.part new file mode 100644 index 0000000000000..54a0e8cb45d19 --- /dev/null +++ b/e2e_test/batch/functions/array_max.slt.part @@ -0,0 +1,72 @@ +query I +select array_max(array[1, 2, 3]); +---- +3 + +query I +select array_max(array[2, 3, 5, 2, 4]); +---- +5 + +query I +select array_max(array[114514, 114513]); +---- +114514 + +query I +select array_max(array['a', 'b', 'c', 'a']); +---- +c + +query I +select array_max(array['e💩a', 'f🤔️b', 'c🥵c', 'd🥳d', 'e💩e']); +---- +f🤔️b + +query I +select array_max(array['2c😅🤔😅️c2', '114🥵514', '30🤣🥳03', '5🥵💩💩🥵5']); +---- +5🥵💩💩🥵5 + +query error invalid digit found in string +select array_max(array['a', 'b', 'c', 114514]); + +query error invalid digit found in string +select array_max(array[114514, 'a', 'b', 'c']); + +# i32::MIN & i32::MIN - 1 & i32::MAX +query I +select array_max(array[-2147483648, 2147483647, -2147483649]); +---- +2147483647 + +# i64::MIN & i64::MIN - 1 & i64::MAX +query I +select array_max(array[-9223372036854775808, 9223372036854775807, -9223372036854775809]); +---- +9223372036854775807 + +query I +select array_max(array['a', '', 'c']); +---- +c + +query I +select array_max(array[3.14, 1.14, 1.14514]); +---- +3.14 + +query I +select array_max(array[3.1415926, 191.14, 114514, 1313.1414]); +---- +114514 + +query I +select array_max(array[1e-4, 1.14514e5, 1.14514e-5]); +---- +114514 + +query I +select array_max(array[date'2002-10-30', date'2023-09-06', date'2017-06-18']); +---- +2023-09-06 \ No newline at end of file diff --git a/proto/expr.proto b/proto/expr.proto index 2dd60bea4f23a..9bd5af893cb8b 100644 --- a/proto/expr.proto +++ b/proto/expr.proto @@ -198,6 +198,7 @@ message ExprNode { ARRAY_DIMS = 544; ARRAY_TRANSFORM = 545; ARRAY_MIN = 546; + ARRAY_MAX = 547; // Int256 functions HEX_TO_INT256 = 560; diff --git a/src/expr/src/sig/func.rs b/src/expr/src/sig/func.rs index 8e943a6ffa710..5dca4da2f4486 100644 --- a/src/expr/src/sig/func.rs +++ b/src/expr/src/sig/func.rs @@ -202,6 +202,9 @@ mod tests { ArrayMin: [ "array_min(list) -> bytea/varchar/timestamptz/timestamp/time/date/int256/serial/decimal/float32/float64/int16/int32/int64", ], + ArrayMax: [ + "array_max(list) -> bytea/varchar/timestamptz/timestamp/time/date/int256/serial/decimal/float32/float64/int16/int32/int64", + ], } "#]]; expected.assert_debug_eq(&duplicated); diff --git a/src/expr/src/vector_op/array_min.rs b/src/expr/src/vector_op/array_min_max.rs similarity index 63% rename from src/expr/src/vector_op/array_min.rs rename to src/expr/src/vector_op/array_min_max.rs index 5e8a6d10e89e3..1ff1a4086f2cd 100644 --- a/src/expr/src/vector_op/array_min.rs +++ b/src/expr/src/vector_op/array_min_max.rs @@ -18,6 +18,8 @@ use risingwave_expr_macro::function; use crate::Result; +/// FIXME: #[`function("array_min(list`) -> any")] supports +/// In this way we could avoid manual macro expansion #[function("array_min(list) -> *int")] #[function("array_min(list) -> *float")] #[function("array_min(list) -> decimal")] @@ -36,3 +38,22 @@ pub fn array_min(list: ListRef<'_>) -> Result> { None => Ok(None), } } + +#[function("array_max(list) -> *int")] +#[function("array_max(list) -> *float")] +#[function("array_max(list) -> decimal")] +#[function("array_max(list) -> serial")] +#[function("array_max(list) -> int256")] +#[function("array_max(list) -> date")] +#[function("array_max(list) -> time")] +#[function("array_max(list) -> timestamp")] +#[function("array_max(list) -> timestamptz")] +#[function("array_max(list) -> varchar")] +#[function("array_max(list) -> bytea")] +pub fn array_max(list: ListRef<'_>) -> Result> { + let max_value = list.iter().flatten().map(DefaultOrdered).max(); + match max_value.map(|v| v.0).to_owned_datum() { + Some(s) => Ok(Some(s.try_into()?)), + None => Ok(None), + } +} diff --git a/src/expr/src/vector_op/mod.rs b/src/expr/src/vector_op/mod.rs index 61aa194f974c9..f4aaa375f8f76 100644 --- a/src/expr/src/vector_op/mod.rs +++ b/src/expr/src/vector_op/mod.rs @@ -16,7 +16,7 @@ pub mod arithmetic_op; pub mod array_access; pub mod array_distinct; pub mod array_length; -pub mod array_min; +pub mod array_min_max; pub mod array_positions; pub mod array_range_access; pub mod array_remove; diff --git a/src/frontend/src/binder/expr/function.rs b/src/frontend/src/binder/expr/function.rs index 54a502ecdea8d..4cb703906bf8c 100644 --- a/src/frontend/src/binder/expr/function.rs +++ b/src/frontend/src/binder/expr/function.rs @@ -793,6 +793,7 @@ impl Binder { ("cardinality", raw_call(ExprType::Cardinality)), ("array_remove", raw_call(ExprType::ArrayRemove)), ("array_replace", raw_call(ExprType::ArrayReplace)), + ("array_max", raw_call(ExprType::ArrayMax)), ("array_position", raw_call(ExprType::ArrayPosition)), ("array_positions", raw_call(ExprType::ArrayPositions)), ("trim_array", raw_call(ExprType::TrimArray)), diff --git a/src/frontend/src/expr/pure.rs b/src/frontend/src/expr/pure.rs index 894d4667a80ac..bb2e1d12d8f87 100644 --- a/src/frontend/src/expr/pure.rs +++ b/src/frontend/src/expr/pure.rs @@ -153,6 +153,7 @@ impl ExprVisitor for ImpureAnalyzer { | expr_node::Type::Row | expr_node::Type::ArrayToString | expr_node::Type::ArrayCat + | expr_node::Type::ArrayMax | expr_node::Type::ArrayAppend | expr_node::Type::ArrayPrepend | expr_node::Type::FormatType diff --git a/src/frontend/src/expr/type_inference/func.rs b/src/frontend/src/expr/type_inference/func.rs index 02c9abaf72b9b..1febc46788af5 100644 --- a/src/frontend/src/expr/type_inference/func.rs +++ b/src/frontend/src/expr/type_inference/func.rs @@ -613,6 +613,12 @@ fn infer_type_for_special( } Ok(Some(DataType::Varchar)) } + ExprType::ArrayMax => { + ensure_arity!("array_max", | inputs | == 1); + inputs[0].ensure_array_type()?; + + Ok(Some(inputs[0].return_type().as_list().clone())) + } ExprType::StringToArray => { ensure_arity!("string_to_array", 2 <= | inputs | <= 3);