Add large number format function

This commit is contained in:
Teja Vegi 2021-10-13 22:43:53 +05:30
parent e16c83d946
commit 69c3ac0805
4 changed files with 208 additions and 1 deletions

View File

@ -104,6 +104,7 @@ import io.prestosql.operator.scalar.DataSizeFunctions;
import io.prestosql.operator.scalar.DateTimeFunctions;
import io.prestosql.operator.scalar.EmptyMapConstructor;
import io.prestosql.operator.scalar.FailureFunction;
import io.prestosql.operator.scalar.FormatNumberFunction;
import io.prestosql.operator.scalar.HmacFunctions;
import io.prestosql.operator.scalar.HyperLogLogFunctions;
import io.prestosql.operator.scalar.JoniRegexpCasts;
@ -496,6 +497,7 @@ public class BuiltInFunctionNamespaceManager
.scalar(MathFunctions.Floor.class)
.scalars(BitwiseFunctions.class)
.scalars(DateTimeFunctions.class)
.scalars(FormatNumberFunction.class)
.scalars(JsonFunctions.class)
.scalars(ColorFunctions.class)
.scalars(ColorOperators.class)

View File

@ -0,0 +1,99 @@
/*
* Copyright (C) 2018-2021. Huawei Technologies Co., Ltd. All rights reserved.
* 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.
*/
package io.prestosql.operator.scalar;
import io.airlift.slice.Slice;
import io.prestosql.spi.function.Description;
import io.prestosql.spi.function.ScalarFunction;
import io.prestosql.spi.function.SqlType;
import io.prestosql.spi.type.StandardTypes;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import static io.airlift.slice.Slices.utf8Slice;
public class FormatNumberFunction
{
private static final DecimalFormat FORMAT_NUMBER_3 = new DecimalFormat("#.##");
private static final DecimalFormat FORMAT_NUMBER_2 = new DecimalFormat("#.#");
private static final DecimalFormat FORMAT_NUMBER_1 = new DecimalFormat("#");
public FormatNumberFunction()
{
FORMAT_NUMBER_3.setRoundingMode(RoundingMode.HALF_UP);
FORMAT_NUMBER_2.setRoundingMode(RoundingMode.HALF_UP);
FORMAT_NUMBER_1.setRoundingMode(RoundingMode.HALF_UP);
}
@ScalarFunction
@Description("Formats large number using a unit symbol")
@SqlType(StandardTypes.VARCHAR)
public Slice formatNumber(@SqlType(StandardTypes.BIGINT) long value)
{
return utf8Slice(format(value));
}
@ScalarFunction
@Description("Formats large number using a unit symbol")
@SqlType(StandardTypes.VARCHAR)
public Slice formatNumber(@SqlType(StandardTypes.DOUBLE) double value)
{
return utf8Slice(format((long) value));
}
private String format(long count)
{
double fractional = count;
String unit = "";
if (fractional >= 1000 || fractional <= -1000) {
fractional /= 1000;
unit = "K";
}
if (fractional >= 1000 || fractional <= -1000) {
fractional /= 1000;
unit = "M";
}
if (fractional >= 1000 || fractional <= -1000) {
fractional /= 1000;
unit = "B";
}
if (fractional >= 1000 || fractional <= -1000) {
fractional /= 1000;
unit = "T";
}
if (fractional >= 1000 || fractional <= -1000) {
fractional /= 1000;
unit = "Q";
}
return getFormat(fractional).format(fractional) + unit;
}
private DecimalFormat getFormat(double value)
{
if (value < 10) {
// show up to two decimals to get 3 significant digits
return FORMAT_NUMBER_3;
}
if (value < 100) {
// show up to one decimal to get 3 significant digits
return FORMAT_NUMBER_2;
}
// show no decimals -- we have enough digits in the integer part
return FORMAT_NUMBER_1;
}
}

View File

@ -57,7 +57,7 @@ public final class ScalarFromAnnotationsParser
ImmutableList.Builder<SqlScalarFunction> builder = ImmutableList.builder();
for (ScalarHeaderAndMethods methods : findScalarsInFunctionSetClass(clazz)) {
// Non-static function only makes sense in classes annotated with @ScalarFunction or @ScalarOperator.
builder.add(parseParametricScalar(methods, Optional.empty()));
builder.add(parseParametricScalar(methods, FunctionsParserHelper.findConstructor(clazz)));
}
return builder.build();
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2018-2021. Huawei Technologies Co., Ltd. All rights reserved.
* 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.
*/
package io.prestosql.operator.scalar;
import org.testng.annotations.Test;
import static io.prestosql.spi.type.VarcharType.VARCHAR;
public class TestFormatNumberFunction
extends AbstractTestFunctions
{
@Test
public void testFormatNumber()
{
assertFunction("format_number(TINYINT '123')", VARCHAR, "123");
assertFunction("format_number(SMALLINT '12345')", VARCHAR, "12.3K");
assertFunction("format_number(SMALLINT '12399')", VARCHAR, "12.4K");
assertFunction("format_number(INTEGER '12345678')", VARCHAR, "12.3M");
assertFunction("format_number(INTEGER '12399999')", VARCHAR, "12.4M");
assertFunction("format_number(BIGINT '12345678901')", VARCHAR, "12.3B");
assertFunction("format_number(BIGINT '12399999999')", VARCHAR, "12.4B");
assertFunction("format_number(DOUBLE '1234.5')", VARCHAR, "1.23K");
assertFunction("format_number(DOUBLE '1239.9')", VARCHAR, "1.24K");
assertFunction("format_number(REAL '1234567.8')", VARCHAR, "1.23M");
assertFunction("format_number(REAL '1239999.9')", VARCHAR, "1.24M");
assertFunction("format_number(DECIMAL '1234567890.1')", VARCHAR, "1.23B");
assertFunction("format_number(DECIMAL '1239999999.9')", VARCHAR, "1.24B");
assertFunction("format_number(-999)", VARCHAR, "-999");
assertFunction("format_number(-1000)", VARCHAR, "-1K");
assertFunction("format_number(-999999)", VARCHAR, "-1000K");
assertFunction("format_number(-1000000)", VARCHAR, "-1M");
assertFunction("format_number(-999999999)", VARCHAR, "-1000M");
assertFunction("format_number(-1000000000)", VARCHAR, "-1B");
assertFunction("format_number(-999999999999)", VARCHAR, "-1000B");
assertFunction("format_number(-1000000000000)", VARCHAR, "-1T");
assertFunction("format_number(-999999999999999)", VARCHAR, "-1000T");
assertFunction("format_number(-1000000000000000)", VARCHAR, "-1Q");
assertFunction("format_number(-9223372036854775808)", VARCHAR, "-9223.37Q");
assertFunction("format_number(0)", VARCHAR, "0");
assertFunction("format_number(999)", VARCHAR, "999");
assertFunction("format_number(1000)", VARCHAR, "1K");
assertFunction("format_number(999999)", VARCHAR, "1000K");
assertFunction("format_number(1000000)", VARCHAR, "1M");
assertFunction("format_number(999999999)", VARCHAR, "1000M");
assertFunction("format_number(1000000000)", VARCHAR, "1B");
assertFunction("format_number(999999999999)", VARCHAR, "1000B");
assertFunction("format_number(1000000000000)", VARCHAR, "1T");
assertFunction("format_number(999999999999999)", VARCHAR, "1000T");
assertFunction("format_number(1000000000000000)", VARCHAR, "1Q");
assertFunction("format_number(9223372036854775807)", VARCHAR, "9223Q");
assertFunction("format_number(CAST(NULL AS TINYINT))", VARCHAR, null);
assertFunction("format_number(CAST(NULL AS SMALLINT))", VARCHAR, null);
assertFunction("format_number(CAST(NULL AS INTEGER))", VARCHAR, null);
assertFunction("format_number(CAST(NULL AS DOUBLE))", VARCHAR, null);
assertFunction("format_number(CAST(NULL AS REAL))", VARCHAR, null);
assertFunction("format_number(CAST(NULL AS DECIMAL))", VARCHAR, null);
}
@Test(expectedExceptions = RuntimeException.class)
public void testVarcharFormatFunction()
{
assertFunction("format_number('abc')", VARCHAR, "");
}
@Test(expectedExceptions = RuntimeException.class)
public void testDateFormatFunction()
{
assertFunction("format_number(DATE '2015-09-01')", VARCHAR, "");
}
@Test(expectedExceptions = RuntimeException.class)
public void testTimestampFormatFunction()
{
assertFunction("format_number(TIMESTAMP '2015-05-10 12:45:31')", VARCHAR, "");
}
@Test(expectedExceptions = RuntimeException.class)
public void testBooleanFormatFunction()
{
assertFunction("format_number(true)", VARCHAR, "");
assertFunction("format_number(false)", VARCHAR, "");
}
@Test(expectedExceptions = RuntimeException.class)
public void testNumberAsVarcharFormatFunction()
{
assertFunction("format_number('123')", VARCHAR, "");
}
}