From d0f278c16c36c2bbac3cbc4de3b3e08b8d7973d2 Mon Sep 17 00:00:00 2001 From: cadl Date: Mon, 25 Apr 2022 00:20:33 +0800 Subject: [PATCH 1/5] feat(function): Add scalar function `humanize` --- .../functions/src/scalars/others/humanize.rs | 83 +++++++++++++++++++ common/functions/src/scalars/others/mod.rs | 2 + common/functions/src/scalars/others/other.rs | 2 + .../tests/it/scalars/strings/humanize.rs | 63 ++++++++++++++ .../functions/tests/it/scalars/strings/mod.rs | 1 + .../120-other-functions/humanize.md | 33 ++++++++ .../02_0055_function_strings_humanize.result | 12 +++ .../02_0055_function_strings_humanize.sql | 12 +++ 8 files changed, 208 insertions(+) create mode 100644 common/functions/src/scalars/others/humanize.rs create mode 100644 common/functions/tests/it/scalars/strings/humanize.rs create mode 100644 docs/doc/30-reference/20-functions/120-other-functions/humanize.md create mode 100644 tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result create mode 100644 tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql diff --git a/common/functions/src/scalars/others/humanize.rs b/common/functions/src/scalars/others/humanize.rs new file mode 100644 index 0000000000000..8f9b6d50cd88d --- /dev/null +++ b/common/functions/src/scalars/others/humanize.rs @@ -0,0 +1,83 @@ +// Copyright 2022 Datafuse 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 std::fmt; + +use common_datavalues::prelude::*; +use common_datavalues::with_match_primitive_type_id; +use common_datavalues::DataTypePtr; +use common_exception::Result; +use common_io::prelude::convert_byte_size; +use num_traits::AsPrimitive; + +use crate::scalars::assert_numeric; +use crate::scalars::scalar_unary_op; +use crate::scalars::EvalContext; +use crate::scalars::Function; +use crate::scalars::FunctionDescription; +use crate::scalars::FunctionFeatures; + +#[derive(Clone)] +pub struct HumanizeFunction { + display_name: String, +} + +impl HumanizeFunction { + pub fn try_create(display_name: &str, args: &[&DataTypePtr]) -> Result> { + assert_numeric(args[0])?; + Ok(Box::new(HumanizeFunction { + display_name: display_name.to_string(), + })) + } + + pub fn desc() -> FunctionDescription { + FunctionDescription::creator(Box::new(Self::try_create)) + .features(FunctionFeatures::default().deterministic().num_arguments(1)) + } +} + +fn humanize(value: S, _ctx: &mut EvalContext) -> Vec +where S: AsPrimitive { + Vec::from(convert_byte_size(value.as_())) +} + +impl Function for HumanizeFunction { + fn name(&self) -> &str { + &*self.display_name + } + + fn return_type(&self) -> DataTypePtr { + Vu8::to_data_type() + } + + fn eval( + &self, + _func_ctx: crate::scalars::FunctionContext, + columns: &common_datavalues::ColumnsWithField, + _input_rows: usize, + ) -> Result { + with_match_primitive_type_id!(columns[0].data_type().data_type_id(), |$F| { + let col = scalar_unary_op::<$F, Vu8, _>(columns[0].column(), humanize, &mut EvalContext::default())?; + Ok(col.arc()) + },{ + unreachable!() + }) + } +} + +impl fmt::Display for HumanizeFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.display_name) + } +} diff --git a/common/functions/src/scalars/others/mod.rs b/common/functions/src/scalars/others/mod.rs index a548274c43801..dbc9a02281c12 100644 --- a/common/functions/src/scalars/others/mod.rs +++ b/common/functions/src/scalars/others/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. mod exists; +mod humanize; mod ignore; mod inet_aton; mod inet_ntoa; @@ -22,6 +23,7 @@ mod sleep; mod type_of; pub use exists::ExistsFunction; +pub use humanize::HumanizeFunction; pub use ignore::IgnoreFunction; pub use inet_aton::InetAtonFunction; pub use inet_aton::TryInetAtonFunction; diff --git a/common/functions/src/scalars/others/other.rs b/common/functions/src/scalars/others/other.rs index 109a96ae6746b..87304a2ff7636 100644 --- a/common/functions/src/scalars/others/other.rs +++ b/common/functions/src/scalars/others/other.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use super::humanize::HumanizeFunction; use super::inet_aton::InetAtonFunction; use super::inet_aton::TryInetAtonFunction; use super::inet_ntoa::InetNtoaFunction; @@ -34,6 +35,7 @@ impl OtherFunction { factory.register("running_difference", RunningDifferenceFunction::desc()); factory.register("ignore", IgnoreFunction::desc()); + factory.register("humanize", HumanizeFunction::desc()); // INET string to number. factory.register("ipv4_string_to_num", InetAtonFunction::desc()); diff --git a/common/functions/tests/it/scalars/strings/humanize.rs b/common/functions/tests/it/scalars/strings/humanize.rs new file mode 100644 index 0000000000000..334d0784ea97a --- /dev/null +++ b/common/functions/tests/it/scalars/strings/humanize.rs @@ -0,0 +1,63 @@ +// Copyright 2022 Datafuse 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 common_datavalues::prelude::*; +use common_exception::Result; + +use crate::scalars::scalar_function_test::test_scalar_functions; +use crate::scalars::scalar_function_test::ScalarFunctionTest; + +#[test] +fn test_humanize_function() -> Result<()> { + let tests = vec![ + ScalarFunctionTest { + name: "humanize(1000)", + columns: vec![Series::from_data(vec![1000_u32])], + expect: Series::from_data(vec!["1 KB"]), + error: "", + }, + ScalarFunctionTest { + name: "humanize(-1000)", + columns: vec![Series::from_data(vec![-1000_i32])], + expect: Series::from_data(vec!["-1 KB"]), + error: "", + }, + ScalarFunctionTest { + name: "humanize('abc')", + columns: vec![Series::from_data(vec!["abc"])], + expect: Series::from_data(vec!["-1 KB"]), + error: "Expected a numeric type, but got String", + }, + ScalarFunctionTest { + name: "humanize(true)", + columns: vec![Series::from_data(vec![true])], + expect: Series::from_data(vec!["-1 KB"]), + error: "Expected a numeric type, but got Boolean", + }, + ]; + + test_scalar_functions("humanize", &tests) +} + +#[test] +fn test_humanize_nullable() -> Result<()> { + let tests = vec![ScalarFunctionTest { + name: "humanize(null)", + columns: vec![Series::from_data(vec![Some(1_000_000_i32), None])], + expect: Series::from_data(vec![Some("1 MB"), None]), + error: "", + }]; + + test_scalar_functions("humanize", &tests) +} diff --git a/common/functions/tests/it/scalars/strings/mod.rs b/common/functions/tests/it/scalars/strings/mod.rs index 911ed1d1497e4..d8b18eea32421 100644 --- a/common/functions/tests/it/scalars/strings/mod.rs +++ b/common/functions/tests/it/scalars/strings/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. // mod locate; +mod humanize; mod locate; mod lower; mod regexp_instr; diff --git a/docs/doc/30-reference/20-functions/120-other-functions/humanize.md b/docs/doc/30-reference/20-functions/120-other-functions/humanize.md new file mode 100644 index 0000000000000..986966a943cce --- /dev/null +++ b/docs/doc/30-reference/20-functions/120-other-functions/humanize.md @@ -0,0 +1,33 @@ +--- +title: HUMANIZE +--- + +Returns the readable size with a suffix(KB, MB, etc). + +## Syntax + +```sql +HUMANIZE(x); +``` + +## Arguments + +| Arguments | Description | +|-----------|----------------------------| +| x | The numerical size. | + + +## Return Type + +String. + +## Examples + +```sql +SELECT HUMANIZE(1000 * 1000) ++-------------------------+ +| HUMANIZE((1000 * 1000)) | ++-------------------------+ +| 1 MB | ++-------------------------+ +``` diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result new file mode 100644 index 0000000000000..1bef001e1a32e --- /dev/null +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result @@ -0,0 +1,12 @@ +1 KB +1 MB +1 GB +1 TB +1 PB +1 EB +1 ZB +1 YB +1000 YB +-1 KB +0 B +NULL diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql new file mode 100644 index 0000000000000..f79c0d1831808 --- /dev/null +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql @@ -0,0 +1,12 @@ +SELECT HUMANIZE(1000); +SELECT HUMANIZE(1000000); +SELECT HUMANIZE(1000000000); +SELECT HUMANIZE(1000000000000); +SELECT HUMANIZE(1000000000000000); +SELECT HUMANIZE(1000000000000000000); +SELECT HUMANIZE(1000000000000000000000); +SELECT HUMANIZE(1000000000000000000000000); +SELECT HUMANIZE(1000000000000000000000000000); +SELECT HUMANIZE(-1000); +SELECT HUMANIZE(0); +SELECT HUMANIZE(NULL); From 244c2cc31947cb5c2465dbe5179ebf447263359e Mon Sep 17 00:00:00 2001 From: cadl Date: Thu, 28 Apr 2022 00:18:55 +0800 Subject: [PATCH 2/5] refactor: Split tests/it/others.rs --- .../scalars/{strings => others}/humanize.rs | 0 .../tests/it/scalars/others/inet_aton.rs | 77 ++++++++ .../tests/it/scalars/others/inet_ntoa.rs | 113 ++++++++++++ .../functions/tests/it/scalars/others/mod.rs | 19 ++ .../running_difference.rs} | 170 +----------------- .../tests/it/scalars/others/type_of.rs | 31 ++++ .../functions/tests/it/scalars/strings/mod.rs | 1 - 7 files changed, 243 insertions(+), 168 deletions(-) rename common/functions/tests/it/scalars/{strings => others}/humanize.rs (100%) create mode 100644 common/functions/tests/it/scalars/others/inet_aton.rs create mode 100644 common/functions/tests/it/scalars/others/inet_ntoa.rs create mode 100644 common/functions/tests/it/scalars/others/mod.rs rename common/functions/tests/it/scalars/{others.rs => others/running_difference.rs} (57%) create mode 100644 common/functions/tests/it/scalars/others/type_of.rs diff --git a/common/functions/tests/it/scalars/strings/humanize.rs b/common/functions/tests/it/scalars/others/humanize.rs similarity index 100% rename from common/functions/tests/it/scalars/strings/humanize.rs rename to common/functions/tests/it/scalars/others/humanize.rs diff --git a/common/functions/tests/it/scalars/others/inet_aton.rs b/common/functions/tests/it/scalars/others/inet_aton.rs new file mode 100644 index 0000000000000..e3d40ff52999e --- /dev/null +++ b/common/functions/tests/it/scalars/others/inet_aton.rs @@ -0,0 +1,77 @@ +// Copyright 2022 Datafuse 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 common_datavalues::prelude::*; +use common_exception::Result; + +use crate::scalars::scalar_function_test::test_scalar_functions; +use crate::scalars::scalar_function_test::ScalarFunctionTest; + +#[test] +fn test_try_inet_aton_function() -> Result<()> { + let tests = vec![ + ScalarFunctionTest { + name: "valid input", + columns: vec![Series::from_data(vec!["127.0.0.1"])], + expect: Series::from_data(vec![Option::::Some(2130706433_u32)]), + error: "", + }, + ScalarFunctionTest { + name: "invalid input", + columns: vec![Series::from_data(vec![Some("invalid")])], + expect: Series::from_data(vec![Option::::None]), + error: "", + }, + ScalarFunctionTest { + name: "null input", + columns: vec![Series::from_data(vec![Option::>::None])], + expect: Series::from_data(vec![Option::::None]), + error: "", + }, + ]; + + test_scalar_functions("try_inet_aton", &tests) +} + +#[test] +fn test_inet_aton_function() -> Result<()> { + let tests = vec![ + ScalarFunctionTest { + name: "valid input", + columns: vec![Series::from_data([Some("127.0.0.1")])], + expect: Series::from_data(vec![Option::::Some(2130706433_u32)]), + error: "", + }, + ScalarFunctionTest { + name: "null input", + columns: vec![Series::from_data([Option::>::None])], + expect: Series::from_data([Option::::None]), + error: "", + }, + ScalarFunctionTest { + name: "invalid input", + columns: vec![Series::from_data([Some("1.1.1.1"), Some("batman")])], + expect: Series::from_data(vec![Option::::None]), + error: "Failed to parse 'batman' into a IPV4 address, invalid IP address syntax", + }, + ScalarFunctionTest { + name: "empty string", + columns: vec![Series::from_data([Some("1.1.1.1"), Some("")])], + expect: Series::from_data(vec![Option::::None]), + error: "Failed to parse '' into a IPV4 address, invalid IP address syntax", + }, + ]; + + test_scalar_functions("inet_aton", &tests) +} diff --git a/common/functions/tests/it/scalars/others/inet_ntoa.rs b/common/functions/tests/it/scalars/others/inet_ntoa.rs new file mode 100644 index 0000000000000..ba43930b9e6d4 --- /dev/null +++ b/common/functions/tests/it/scalars/others/inet_ntoa.rs @@ -0,0 +1,113 @@ +// Copyright 2022 Datafuse 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 common_datavalues::prelude::*; +use common_exception::Result; + +use crate::scalars::scalar_function_test::test_scalar_functions; +use crate::scalars::scalar_function_test::ScalarFunctionTest; + +#[test] +fn test_try_inet_ntoa_function() -> Result<()> { + let tests = vec![ + // integer input test cases + ScalarFunctionTest { + name: "integer_input_i32_positive", + columns: vec![Series::from_data(vec![2130706433_i32])], + expect: Series::from_data(vec![Some("127.0.0.1")]), + error: "", + }, + ScalarFunctionTest { + name: "integer_input_i32_negative", + columns: vec![Series::from_data(vec![-1])], + expect: Series::from_data(vec![Some("255.255.255.255")]), + error: "", + }, + ScalarFunctionTest { + name: "integer_input_u8", + columns: vec![Series::from_data(vec![Some(0_u8)])], + expect: Series::from_data(vec![Some("0.0.0.0")]), + error: "", + }, + ScalarFunctionTest { + name: "integer_input_u32", + columns: vec![Series::from_data(vec![Some(3232235777_u32)])], + expect: Series::from_data(vec![Some("192.168.1.1")]), + error: "", + }, + // float input test cases + ScalarFunctionTest { + name: "float_input_f64", + columns: vec![Series::from_data(vec![2130706433.3917_f64])], + expect: Series::from_data(vec![Some("127.0.0.1")]), + error: "", + }, + // string input test cases + ScalarFunctionTest { + name: "string_input_u32", + columns: vec![Series::from_data(vec!["3232235777"])], + expect: Series::from_data(vec![Some("192.168.1.1")]), + error: "Expected a numeric type, but got String", + }, + ]; + + test_scalar_functions("try_inet_ntoa", &tests) +} + +#[test] +fn test_inet_ntoa_function() -> Result<()> { + let tests = vec![ + // integer input test cases + ScalarFunctionTest { + name: "integer_input_i32_positive", + columns: vec![Series::from_data([2130706433_i32])], + expect: Series::from_data(["127.0.0.1"]), + error: "", + }, + ScalarFunctionTest { + name: "integer_input_i32_negative", + columns: vec![Series::from_data([-1])], + expect: Series::from_data(["255.255.255.255"]), + error: "", + }, + ScalarFunctionTest { + name: "integer_input_u8", + columns: vec![Series::from_data([Some(0_u8)])], + expect: Series::from_data([Some("0.0.0.0")]), + error: "", + }, + ScalarFunctionTest { + name: "integer_input_u32", + columns: vec![Series::from_data([Some(3232235777_u32)])], + expect: Series::from_data([Some("192.168.1.1")]), + error: "", + }, + // float input test cases + ScalarFunctionTest { + name: "float_input_f64", + columns: vec![Series::from_data([2130706433.3917_f64])], + expect: Series::from_data(["127.0.0.1"]), + error: "", + }, + // string input test cases + ScalarFunctionTest { + name: "string_input_empty", + columns: vec![Series::from_data([""])], + expect: Series::from_data([""]), + error: "Expected a numeric type, but got String", + }, + ]; + + test_scalar_functions("inet_ntoa", &tests) +} diff --git a/common/functions/tests/it/scalars/others/mod.rs b/common/functions/tests/it/scalars/others/mod.rs new file mode 100644 index 0000000000000..461fd9451ee1f --- /dev/null +++ b/common/functions/tests/it/scalars/others/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2022 Datafuse 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. + +mod humanize; +mod inet_aton; +mod inet_ntoa; +mod running_difference; +mod type_of; diff --git a/common/functions/tests/it/scalars/others.rs b/common/functions/tests/it/scalars/others/running_difference.rs similarity index 57% rename from common/functions/tests/it/scalars/others.rs rename to common/functions/tests/it/scalars/others/running_difference.rs index a6ef2773b8ab4..510bd789c1044 100644 --- a/common/functions/tests/it/scalars/others.rs +++ b/common/functions/tests/it/scalars/others/running_difference.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Datafuse Labs. +// Copyright 2022 Datafuse Labs // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,9 +15,9 @@ use common_datavalues::prelude::*; use common_exception::Result; -use super::scalar_function_test::test_scalar_functions; -use super::scalar_function_test::ScalarFunctionTest; +use crate::scalars::scalar_function_test::test_scalar_functions; use crate::scalars::scalar_function_test::test_scalar_functions_with_type; +use crate::scalars::scalar_function_test::ScalarFunctionTest; use crate::scalars::scalar_function_test::ScalarFunctionWithFieldTest; #[test] @@ -251,167 +251,3 @@ fn test_running_difference_datetime32_first_null() -> Result<()> { test_scalar_functions_with_type("running_difference", &tests) } - -#[test] -fn test_try_inet_aton_function() -> Result<()> { - let tests = vec![ - ScalarFunctionTest { - name: "valid input", - columns: vec![Series::from_data(vec!["127.0.0.1"])], - expect: Series::from_data(vec![Option::::Some(2130706433_u32)]), - error: "", - }, - ScalarFunctionTest { - name: "invalid input", - columns: vec![Series::from_data(vec![Some("invalid")])], - expect: Series::from_data(vec![Option::::None]), - error: "", - }, - ScalarFunctionTest { - name: "null input", - columns: vec![Series::from_data(vec![Option::>::None])], - expect: Series::from_data(vec![Option::::None]), - error: "", - }, - ]; - - test_scalar_functions("try_inet_aton", &tests) -} - -#[test] -fn test_inet_aton_function() -> Result<()> { - let tests = vec![ - ScalarFunctionTest { - name: "valid input", - columns: vec![Series::from_data([Some("127.0.0.1")])], - expect: Series::from_data(vec![Option::::Some(2130706433_u32)]), - error: "", - }, - ScalarFunctionTest { - name: "null input", - columns: vec![Series::from_data([Option::>::None])], - expect: Series::from_data([Option::::None]), - error: "", - }, - ScalarFunctionTest { - name: "invalid input", - columns: vec![Series::from_data([Some("1.1.1.1"), Some("batman")])], - expect: Series::from_data(vec![Option::::None]), - error: "Failed to parse 'batman' into a IPV4 address, invalid IP address syntax", - }, - ScalarFunctionTest { - name: "empty string", - columns: vec![Series::from_data([Some("1.1.1.1"), Some("")])], - expect: Series::from_data(vec![Option::::None]), - error: "Failed to parse '' into a IPV4 address, invalid IP address syntax", - }, - ]; - - test_scalar_functions("inet_aton", &tests) -} - -#[test] -fn test_try_inet_ntoa_function() -> Result<()> { - let tests = vec![ - // integer input test cases - ScalarFunctionTest { - name: "integer_input_i32_positive", - columns: vec![Series::from_data(vec![2130706433_i32])], - expect: Series::from_data(vec![Some("127.0.0.1")]), - error: "", - }, - ScalarFunctionTest { - name: "integer_input_i32_negative", - columns: vec![Series::from_data(vec![-1])], - expect: Series::from_data(vec![Some("255.255.255.255")]), - error: "", - }, - ScalarFunctionTest { - name: "integer_input_u8", - columns: vec![Series::from_data(vec![Some(0_u8)])], - expect: Series::from_data(vec![Some("0.0.0.0")]), - error: "", - }, - ScalarFunctionTest { - name: "integer_input_u32", - columns: vec![Series::from_data(vec![Some(3232235777_u32)])], - expect: Series::from_data(vec![Some("192.168.1.1")]), - error: "", - }, - // float input test cases - ScalarFunctionTest { - name: "float_input_f64", - columns: vec![Series::from_data(vec![2130706433.3917_f64])], - expect: Series::from_data(vec![Some("127.0.0.1")]), - error: "", - }, - // string input test cases - ScalarFunctionTest { - name: "string_input_u32", - columns: vec![Series::from_data(vec!["3232235777"])], - expect: Series::from_data(vec![Some("192.168.1.1")]), - error: "Expected a numeric type, but got String", - }, - ]; - - test_scalar_functions("try_inet_ntoa", &tests) -} - -#[test] -fn test_inet_ntoa_function() -> Result<()> { - let tests = vec![ - // integer input test cases - ScalarFunctionTest { - name: "integer_input_i32_positive", - columns: vec![Series::from_data([2130706433_i32])], - expect: Series::from_data(["127.0.0.1"]), - error: "", - }, - ScalarFunctionTest { - name: "integer_input_i32_negative", - columns: vec![Series::from_data([-1])], - expect: Series::from_data(["255.255.255.255"]), - error: "", - }, - ScalarFunctionTest { - name: "integer_input_u8", - columns: vec![Series::from_data([Some(0_u8)])], - expect: Series::from_data([Some("0.0.0.0")]), - error: "", - }, - ScalarFunctionTest { - name: "integer_input_u32", - columns: vec![Series::from_data([Some(3232235777_u32)])], - expect: Series::from_data([Some("192.168.1.1")]), - error: "", - }, - // float input test cases - ScalarFunctionTest { - name: "float_input_f64", - columns: vec![Series::from_data([2130706433.3917_f64])], - expect: Series::from_data(["127.0.0.1"]), - error: "", - }, - // string input test cases - ScalarFunctionTest { - name: "string_input_empty", - columns: vec![Series::from_data([""])], - expect: Series::from_data([""]), - error: "Expected a numeric type, but got String", - }, - ]; - - test_scalar_functions("inet_ntoa", &tests) -} - -#[test] -fn test_type_of_function() -> Result<()> { - let tests = vec![ScalarFunctionTest { - name: "type-of-example-passed", - columns: vec![Series::from_data([true, true, true, false])], - expect: Series::from_data(["BOOLEAN", "BOOLEAN", "BOOLEAN", "BOOLEAN"]), - error: "", - }]; - - test_scalar_functions("typeof", &tests) -} diff --git a/common/functions/tests/it/scalars/others/type_of.rs b/common/functions/tests/it/scalars/others/type_of.rs new file mode 100644 index 0000000000000..77e09d64606ea --- /dev/null +++ b/common/functions/tests/it/scalars/others/type_of.rs @@ -0,0 +1,31 @@ +// Copyright 2022 Datafuse 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 common_datavalues::prelude::*; +use common_exception::Result; + +use crate::scalars::scalar_function_test::test_scalar_functions; +use crate::scalars::scalar_function_test::ScalarFunctionTest; + +#[test] +fn test_type_of_function() -> Result<()> { + let tests = vec![ScalarFunctionTest { + name: "type-of-example-passed", + columns: vec![Series::from_data([true, true, true, false])], + expect: Series::from_data(["BOOLEAN", "BOOLEAN", "BOOLEAN", "BOOLEAN"]), + error: "", + }]; + + test_scalar_functions("typeof", &tests) +} diff --git a/common/functions/tests/it/scalars/strings/mod.rs b/common/functions/tests/it/scalars/strings/mod.rs index d8b18eea32421..911ed1d1497e4 100644 --- a/common/functions/tests/it/scalars/strings/mod.rs +++ b/common/functions/tests/it/scalars/strings/mod.rs @@ -13,7 +13,6 @@ // limitations under the License. // mod locate; -mod humanize; mod locate; mod lower; mod regexp_instr; From ef16ce9dd25cd4b2aae5b729810a331132390805 Mon Sep 17 00:00:00 2001 From: cadl Date: Fri, 29 Apr 2022 17:31:07 +0800 Subject: [PATCH 3/5] feat(function): Add `humanize_size` and `humanize_number` functions --- .../functions/src/scalars/others/humanize.rs | 56 ++++++++++++----- common/functions/src/scalars/others/mod.rs | 3 +- common/functions/src/scalars/others/other.rs | 6 +- .../tests/it/scalars/others/humanize.rs | 62 ++++++++++++++++--- .../120-other-functions/humanize-number.md | 33 ++++++++++ .../{humanize.md => humanize-size.md} | 8 +-- .../02_0055_function_strings_humanize.result | 9 +++ .../02_0055_function_strings_humanize.sql | 33 ++++++---- 8 files changed, 167 insertions(+), 43 deletions(-) create mode 100644 docs/doc/30-reference/20-functions/120-other-functions/humanize-number.md rename docs/doc/30-reference/20-functions/120-other-functions/{humanize.md => humanize-size.md} (78%) diff --git a/common/functions/src/scalars/others/humanize.rs b/common/functions/src/scalars/others/humanize.rs index 8f9b6d50cd88d..9c0dfcd39f383 100644 --- a/common/functions/src/scalars/others/humanize.rs +++ b/common/functions/src/scalars/others/humanize.rs @@ -13,12 +13,12 @@ // limitations under the License. use std::fmt; +use std::marker::PhantomData; use common_datavalues::prelude::*; use common_datavalues::with_match_primitive_type_id; -use common_datavalues::DataTypePtr; use common_exception::Result; -use common_io::prelude::convert_byte_size; +use common_io::prelude::*; use num_traits::AsPrimitive; use crate::scalars::assert_numeric; @@ -29,15 +29,23 @@ use crate::scalars::FunctionDescription; use crate::scalars::FunctionFeatures; #[derive(Clone)] -pub struct HumanizeFunction { +pub struct GenericHumanizeFunction { display_name: String, + t: PhantomData, } -impl HumanizeFunction { - pub fn try_create(display_name: &str, args: &[&DataTypePtr]) -> Result> { +pub trait HumanizeConvertFunction: Send + Sync + Clone + 'static { + fn convert(v: impl AsPrimitive, _ctx: &mut EvalContext) -> Vec; +} + +impl GenericHumanizeFunction +where T: HumanizeConvertFunction +{ + pub fn try_create(display_name: &str, args: &[&DataTypeImpl]) -> Result> { assert_numeric(args[0])?; - Ok(Box::new(HumanizeFunction { + Ok(Box::new(GenericHumanizeFunction:: { display_name: display_name.to_string(), + t: PhantomData, })) } @@ -47,17 +55,14 @@ impl HumanizeFunction { } } -fn humanize(value: S, _ctx: &mut EvalContext) -> Vec -where S: AsPrimitive { - Vec::from(convert_byte_size(value.as_())) -} - -impl Function for HumanizeFunction { +impl Function for GenericHumanizeFunction +where T: HumanizeConvertFunction +{ fn name(&self) -> &str { &*self.display_name } - fn return_type(&self) -> DataTypePtr { + fn return_type(&self) -> DataTypeImpl { Vu8::to_data_type() } @@ -68,7 +73,7 @@ impl Function for HumanizeFunction { _input_rows: usize, ) -> Result { with_match_primitive_type_id!(columns[0].data_type().data_type_id(), |$F| { - let col = scalar_unary_op::<$F, Vu8, _>(columns[0].column(), humanize, &mut EvalContext::default())?; + let col = scalar_unary_op::<$F, Vu8, _>(columns[0].column(), T::convert, &mut EvalContext::default())?; Ok(col.arc()) },{ unreachable!() @@ -76,8 +81,29 @@ impl Function for HumanizeFunction { } } -impl fmt::Display for HumanizeFunction { +impl fmt::Display for GenericHumanizeFunction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.display_name) } } + +#[derive(Clone)] +pub struct HumanizeSizeConvertFunction; + +impl HumanizeConvertFunction for HumanizeSizeConvertFunction { + fn convert(v: impl AsPrimitive, _: &mut EvalContext) -> Vec { + Vec::from(convert_byte_size(v.as_())) + } +} + +#[derive(Clone)] +pub struct HumanizeNumberConvertFunction; + +impl HumanizeConvertFunction for HumanizeNumberConvertFunction { + fn convert(v: impl AsPrimitive, _: &mut EvalContext) -> Vec { + Vec::from(convert_number_size(v.as_())) + } +} + +pub type HumanizeSizeFunction = GenericHumanizeFunction; +pub type HumanizeNumberFunction = GenericHumanizeFunction; diff --git a/common/functions/src/scalars/others/mod.rs b/common/functions/src/scalars/others/mod.rs index dbc9a02281c12..f0cd8500b8cb3 100644 --- a/common/functions/src/scalars/others/mod.rs +++ b/common/functions/src/scalars/others/mod.rs @@ -23,7 +23,8 @@ mod sleep; mod type_of; pub use exists::ExistsFunction; -pub use humanize::HumanizeFunction; +pub use humanize::HumanizeNumberFunction; +pub use humanize::HumanizeSizeFunction; pub use ignore::IgnoreFunction; pub use inet_aton::InetAtonFunction; pub use inet_aton::TryInetAtonFunction; diff --git a/common/functions/src/scalars/others/other.rs b/common/functions/src/scalars/others/other.rs index 87304a2ff7636..cd3065828be78 100644 --- a/common/functions/src/scalars/others/other.rs +++ b/common/functions/src/scalars/others/other.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::humanize::HumanizeFunction; +use super::humanize::HumanizeNumberFunction; +use super::humanize::HumanizeSizeFunction; use super::inet_aton::InetAtonFunction; use super::inet_aton::TryInetAtonFunction; use super::inet_ntoa::InetNtoaFunction; @@ -35,7 +36,8 @@ impl OtherFunction { factory.register("running_difference", RunningDifferenceFunction::desc()); factory.register("ignore", IgnoreFunction::desc()); - factory.register("humanize", HumanizeFunction::desc()); + factory.register("humanize_size", HumanizeSizeFunction::desc()); + factory.register("humanize_number", HumanizeNumberFunction::desc()); // INET string to number. factory.register("ipv4_string_to_num", InetAtonFunction::desc()); diff --git a/common/functions/tests/it/scalars/others/humanize.rs b/common/functions/tests/it/scalars/others/humanize.rs index 334d0784ea97a..cb5614c4569eb 100644 --- a/common/functions/tests/it/scalars/others/humanize.rs +++ b/common/functions/tests/it/scalars/others/humanize.rs @@ -19,45 +19,89 @@ use crate::scalars::scalar_function_test::test_scalar_functions; use crate::scalars::scalar_function_test::ScalarFunctionTest; #[test] -fn test_humanize_function() -> Result<()> { +fn test_humanize_size_function() -> Result<()> { let tests = vec![ ScalarFunctionTest { - name: "humanize(1000)", + name: "humanize_size(1000)", columns: vec![Series::from_data(vec![1000_u32])], expect: Series::from_data(vec!["1 KB"]), error: "", }, ScalarFunctionTest { - name: "humanize(-1000)", + name: "humanize_size(-1000)", columns: vec![Series::from_data(vec![-1000_i32])], expect: Series::from_data(vec!["-1 KB"]), error: "", }, ScalarFunctionTest { - name: "humanize('abc')", + name: "humanize_size('abc')", columns: vec![Series::from_data(vec!["abc"])], expect: Series::from_data(vec!["-1 KB"]), error: "Expected a numeric type, but got String", }, ScalarFunctionTest { - name: "humanize(true)", + name: "humanize_size(true)", columns: vec![Series::from_data(vec![true])], expect: Series::from_data(vec!["-1 KB"]), error: "Expected a numeric type, but got Boolean", }, ]; - test_scalar_functions("humanize", &tests) + test_scalar_functions("humanize_size", &tests) } #[test] -fn test_humanize_nullable() -> Result<()> { +fn test_humanize_size_nullable() -> Result<()> { let tests = vec![ScalarFunctionTest { - name: "humanize(null)", + name: "humanize_size(null)", columns: vec![Series::from_data(vec![Some(1_000_000_i32), None])], expect: Series::from_data(vec![Some("1 MB"), None]), error: "", }]; - test_scalar_functions("humanize", &tests) + test_scalar_functions("humanize_size", &tests) +} + +#[test] +fn test_humanize_number_function() -> Result<()> { + let tests = vec![ + ScalarFunctionTest { + name: "humanize_number(1000)", + columns: vec![Series::from_data(vec![1000_u32])], + expect: Series::from_data(vec!["1 thousand"]), + error: "", + }, + ScalarFunctionTest { + name: "humanize_number(-1000)", + columns: vec![Series::from_data(vec![-1000_i32])], + expect: Series::from_data(vec!["-1 thousand"]), + error: "", + }, + ScalarFunctionTest { + name: "humanize_number('abc')", + columns: vec![Series::from_data(vec!["abc"])], + expect: Series::from_data(vec!["-1 thousand"]), + error: "Expected a numeric type, but got String", + }, + ScalarFunctionTest { + name: "humanize_number(true)", + columns: vec![Series::from_data(vec![true])], + expect: Series::from_data(vec!["-1 thousand"]), + error: "Expected a numeric type, but got Boolean", + }, + ]; + + test_scalar_functions("humanize_number", &tests) +} + +#[test] +fn test_humanize_number_nullable() -> Result<()> { + let tests = vec![ScalarFunctionTest { + name: "humanize_number(null)", + columns: vec![Series::from_data(vec![Some(1_000_000_i32), None])], + expect: Series::from_data(vec![Some("1 million"), None]), + error: "", + }]; + + test_scalar_functions("humanize_number", &tests) } diff --git a/docs/doc/30-reference/20-functions/120-other-functions/humanize-number.md b/docs/doc/30-reference/20-functions/120-other-functions/humanize-number.md new file mode 100644 index 0000000000000..52d2151ad8173 --- /dev/null +++ b/docs/doc/30-reference/20-functions/120-other-functions/humanize-number.md @@ -0,0 +1,33 @@ +--- +title: HUMANIZE_NUMBER +--- + +Returns a readable number. + +## Syntax + +```sql +HUMANIZE_NUMBER(x); +``` + +## Arguments + +| Arguments | Description | +|-----------|----------------------------| +| x | The numerical size. | + + +## Return Type + +String. + +## Examples + +```sql +SELECT HUMANIZE_NUMBER(1000 * 1000) ++-------------------------+ +| HUMANIZE_NUMBER((1000 * 1000)) | ++-------------------------+ +| 1 million | ++-------------------------+ +``` diff --git a/docs/doc/30-reference/20-functions/120-other-functions/humanize.md b/docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md similarity index 78% rename from docs/doc/30-reference/20-functions/120-other-functions/humanize.md rename to docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md index 986966a943cce..2578c9620f06e 100644 --- a/docs/doc/30-reference/20-functions/120-other-functions/humanize.md +++ b/docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md @@ -1,5 +1,5 @@ --- -title: HUMANIZE +title: HUMANIZE_SIZE --- Returns the readable size with a suffix(KB, MB, etc). @@ -7,7 +7,7 @@ Returns the readable size with a suffix(KB, MB, etc). ## Syntax ```sql -HUMANIZE(x); +HUMANIZE_SIZE(x); ``` ## Arguments @@ -24,9 +24,9 @@ String. ## Examples ```sql -SELECT HUMANIZE(1000 * 1000) +SELECT HUMANIZE_SIZE(1000 * 1000) +-------------------------+ -| HUMANIZE((1000 * 1000)) | +| HUMANIZE_SIZE((1000 * 1000)) | +-------------------------+ | 1 MB | +-------------------------+ diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result index 1bef001e1a32e..fe9f8ffc7c733 100644 --- a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result @@ -10,3 +10,12 @@ -1 KB 0 B NULL +1 +1 million +1 billion +1 trillion +1 quadrillion +1000 quadrillion +-1000 +0 +NULL diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql index f79c0d1831808..912c869a479c2 100644 --- a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql @@ -1,12 +1,21 @@ -SELECT HUMANIZE(1000); -SELECT HUMANIZE(1000000); -SELECT HUMANIZE(1000000000); -SELECT HUMANIZE(1000000000000); -SELECT HUMANIZE(1000000000000000); -SELECT HUMANIZE(1000000000000000000); -SELECT HUMANIZE(1000000000000000000000); -SELECT HUMANIZE(1000000000000000000000000); -SELECT HUMANIZE(1000000000000000000000000000); -SELECT HUMANIZE(-1000); -SELECT HUMANIZE(0); -SELECT HUMANIZE(NULL); +SELECT HUMANIZE_SIZE(1000); +SELECT HUMANIZE_SIZE(1000000); +SELECT HUMANIZE_SIZE(1000000000); +SELECT HUMANIZE_SIZE(1000000000000); +SELECT HUMANIZE_SIZE(1000000000000000); +SELECT HUMANIZE_SIZE(1000000000000000000); +SELECT HUMANIZE_SIZE(1000000000000000000000); +SELECT HUMANIZE_SIZE(1000000000000000000000000); +SELECT HUMANIZE_SIZE(1000000000000000000000000000); +SELECT HUMANIZE_SIZE(-1000); +SELECT HUMANIZE_SIZE(0); +SELECT HUMANIZE_SIZE(NULL); +SELECT HUMANIZE_NUMBER(1); +SELECT HUMANIZE_NUMBER(1000); +SELECT HUMANIZE_NUMBER(1000000); +SELECT HUMANIZE_NUMBER(1000000000); +SELECT HUMANIZE_NUMBER(1000000000000); +SELECT HUMANIZE_NUMBER(1000000000000000); +SELECT HUMANIZE_NUMBER(-1000); +SELECT HUMANIZE_NUMBER(0); +SELECT HUMANIZE_NUMBER(NULL); From 0fd35770b1fe24408f6bfbceee43d8dc964e7263 Mon Sep 17 00:00:00 2001 From: cadl Date: Fri, 29 Apr 2022 21:33:04 +0800 Subject: [PATCH 4/5] test: make stateless-test happy --- .../02_function/02_0055_function_strings_humanize.result | 3 ++- .../02_function/02_0055_function_strings_humanize.sql | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result index fe9f8ffc7c733..3d20294cfb833 100644 --- a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result @@ -11,11 +11,12 @@ 0 B NULL 1 +1 thousand 1 million 1 billion 1 trillion 1 quadrillion 1000 quadrillion --1000 +-1 thousand 0 NULL diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql index 912c869a479c2..2d5e3248a6c61 100644 --- a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql @@ -16,6 +16,7 @@ SELECT HUMANIZE_NUMBER(1000000); SELECT HUMANIZE_NUMBER(1000000000); SELECT HUMANIZE_NUMBER(1000000000000); SELECT HUMANIZE_NUMBER(1000000000000000); +SELECT HUMANIZE_NUMBER(1000000000000000000); SELECT HUMANIZE_NUMBER(-1000); SELECT HUMANIZE_NUMBER(0); SELECT HUMANIZE_NUMBER(NULL); From 293da8efc284f787bbad114d4460bc75aa3e7d77 Mon Sep 17 00:00:00 2001 From: cadl Date: Tue, 3 May 2022 16:24:22 +0800 Subject: [PATCH 5/5] feat(function): Use the "IEC 80000-13" to replace the decimal based unit kB/KB/MB always leads to a lot of misunderstandings, we just use the KiB --- .../tests/it/scalars/others/humanize.rs | 20 ++++++++-------- common/io/src/utils.rs | 11 ++++----- common/io/tests/it/utils.rs | 13 ++++++++--- .../120-other-functions/humanize-size.md | 8 +++---- .../02_0055_function_strings_humanize.result | 23 ++++++++++--------- .../02_0055_function_strings_humanize.sql | 21 +++++++++-------- 6 files changed, 51 insertions(+), 45 deletions(-) diff --git a/common/functions/tests/it/scalars/others/humanize.rs b/common/functions/tests/it/scalars/others/humanize.rs index cb5614c4569eb..9d456c205bb59 100644 --- a/common/functions/tests/it/scalars/others/humanize.rs +++ b/common/functions/tests/it/scalars/others/humanize.rs @@ -22,27 +22,27 @@ use crate::scalars::scalar_function_test::ScalarFunctionTest; fn test_humanize_size_function() -> Result<()> { let tests = vec![ ScalarFunctionTest { - name: "humanize_size(1000)", - columns: vec![Series::from_data(vec![1000_u32])], - expect: Series::from_data(vec!["1 KB"]), + name: "humanize_size(1024)", + columns: vec![Series::from_data(vec![1024_u32])], + expect: Series::from_data(vec!["1.00 KiB"]), error: "", }, ScalarFunctionTest { - name: "humanize_size(-1000)", - columns: vec![Series::from_data(vec![-1000_i32])], - expect: Series::from_data(vec!["-1 KB"]), + name: "humanize_size(-1024)", + columns: vec![Series::from_data(vec![-1024_i32])], + expect: Series::from_data(vec!["-1.00 KiB"]), error: "", }, ScalarFunctionTest { name: "humanize_size('abc')", columns: vec![Series::from_data(vec!["abc"])], - expect: Series::from_data(vec!["-1 KB"]), + expect: Series::from_data(vec!["-1 KiB"]), error: "Expected a numeric type, but got String", }, ScalarFunctionTest { name: "humanize_size(true)", columns: vec![Series::from_data(vec![true])], - expect: Series::from_data(vec!["-1 KB"]), + expect: Series::from_data(vec!["-1 KiB"]), error: "Expected a numeric type, but got Boolean", }, ]; @@ -54,8 +54,8 @@ fn test_humanize_size_function() -> Result<()> { fn test_humanize_size_nullable() -> Result<()> { let tests = vec![ScalarFunctionTest { name: "humanize_size(null)", - columns: vec![Series::from_data(vec![Some(1_000_000_i32), None])], - expect: Series::from_data(vec![Some("1 MB"), None]), + columns: vec![Series::from_data(vec![Some(1_048_576_i32), None])], + expect: Series::from_data(vec![Some("1.00 MiB"), None]), error: "", }]; diff --git a/common/io/src/utils.rs b/common/io/src/utils.rs index 594a2067f5852..dedd6a5e54524 100644 --- a/common/io/src/utils.rs +++ b/common/io/src/utils.rs @@ -22,19 +22,16 @@ use common_exception::Result; pub fn convert_byte_size(num: f64) -> String { let negative = if num.is_sign_positive() { "" } else { "-" }; let num = num.abs(); - let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + let units = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; if num < 1_f64 { - return format!("{}{} {}", negative, num, "B"); + return format!("{}{:.02} {}", negative, num, "B"); } - let delimiter = 1000_f64; + let delimiter = 1024_f64; let exponent = cmp::min( (num.ln() / delimiter.ln()).floor() as i32, (units.len() - 1) as i32, ); - let pretty_bytes = format!("{:.2}", num / delimiter.powi(exponent)) - .parse::() - .unwrap() - * 1_f64; + let pretty_bytes = format!("{:.02}", num / delimiter.powi(exponent)); let unit = units[exponent as usize]; format!("{}{} {}", negative, pretty_bytes, unit) } diff --git a/common/io/tests/it/utils.rs b/common/io/tests/it/utils.rs index 03804b73857ba..3f6e456f45648 100644 --- a/common/io/tests/it/utils.rs +++ b/common/io/tests/it/utils.rs @@ -16,9 +16,16 @@ use common_io::prelude::*; #[test] fn convert_test() { - assert_eq!(convert_byte_size(1_f64), "1 B"); - assert_eq!(convert_byte_size(1022_f64), "1.02 KB"); - assert_eq!(convert_byte_size(1022_f64 * 10000000f64), "10.22 GB"); + assert_eq!(convert_byte_size(0_f64), "0.00 B"); + assert_eq!(convert_byte_size(0.1_f64), "0.10 B"); + assert_eq!(convert_byte_size(1_f64), "1.00 B"); + assert_eq!(convert_byte_size(1023_f64), "1023.00 B"); + assert_eq!(convert_byte_size(1024_f64), "1.00 KiB"); + assert_eq!(convert_byte_size(1229_f64), "1.20 KiB"); + assert_eq!( + convert_byte_size(1024_f64 * 1024_f64 * 1024_f64), + "1.00 GiB" + ); assert_eq!(convert_number_size(1_f64), "1"); assert_eq!(convert_number_size(1022_f64), "1.02 thousand"); diff --git a/docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md b/docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md index 2578c9620f06e..0347799863542 100644 --- a/docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md +++ b/docs/doc/30-reference/20-functions/120-other-functions/humanize-size.md @@ -2,7 +2,7 @@ title: HUMANIZE_SIZE --- -Returns the readable size with a suffix(KB, MB, etc). +Returns the readable size with a suffix(KiB, MiB, etc). ## Syntax @@ -24,10 +24,10 @@ String. ## Examples ```sql -SELECT HUMANIZE_SIZE(1000 * 1000) +SELECT HUMANIZE_SIZE(1024 * 1024) +-------------------------+ -| HUMANIZE_SIZE((1000 * 1000)) | +| HUMANIZE_SIZE((1024 * 1024)) | +-------------------------+ -| 1 MB | +| 1 MiB | +-------------------------+ ``` diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result index 3d20294cfb833..1adcd56cad5a5 100644 --- a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.result @@ -1,14 +1,15 @@ -1 KB -1 MB -1 GB -1 TB -1 PB -1 EB -1 ZB -1 YB -1000 YB --1 KB -0 B +1.00 KiB +1.20 KiB +1.00 MiB +1.00 GiB +1.00 TiB +1.00 PiB +1.00 EiB +1.00 ZiB +1.00 YiB +1024.00 YiB +-1.00 KiB +0.00 B NULL 1 1 thousand diff --git a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql index 2d5e3248a6c61..0dad64d849291 100644 --- a/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql +++ b/tests/suites/0_stateless/02_function/02_0055_function_strings_humanize.sql @@ -1,13 +1,14 @@ -SELECT HUMANIZE_SIZE(1000); -SELECT HUMANIZE_SIZE(1000000); -SELECT HUMANIZE_SIZE(1000000000); -SELECT HUMANIZE_SIZE(1000000000000); -SELECT HUMANIZE_SIZE(1000000000000000); -SELECT HUMANIZE_SIZE(1000000000000000000); -SELECT HUMANIZE_SIZE(1000000000000000000000); -SELECT HUMANIZE_SIZE(1000000000000000000000000); -SELECT HUMANIZE_SIZE(1000000000000000000000000000); -SELECT HUMANIZE_SIZE(-1000); +SELECT HUMANIZE_SIZE(1024); +SELECT HUMANIZE_SIZE(1229); +SELECT HUMANIZE_SIZE(POW(2, 20)); +SELECT HUMANIZE_SIZE(POW(2, 30)); +SELECT HUMANIZE_SIZE(POW(2, 40)); +SELECT HUMANIZE_SIZE(POW(2, 50)); +SELECT HUMANIZE_SIZE(POW(2, 60)); +SELECT HUMANIZE_SIZE(POW(2, 70)); +SELECT HUMANIZE_SIZE(POW(2, 80)); +SELECT HUMANIZE_SIZE(POW(2, 90)); +SELECT HUMANIZE_SIZE(-1024); SELECT HUMANIZE_SIZE(0); SELECT HUMANIZE_SIZE(NULL); SELECT HUMANIZE_NUMBER(1);