This is an automated email from the ASF dual-hosted git repository.
gian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git
The following commit(s) were added to refs/heads/master by this push:
new 7c19c92 Add ROUND function in druid-sql. (#7224)
7c19c92 is described below
commit 7c19c92a81d33a510cd93f9bd4a448334204982d
Author: Kazuhito Takeuchi <[email protected]>
AuthorDate: Wed Apr 17 03:15:39 2019 +0900
Add ROUND function in druid-sql. (#7224)
* Implement round function in druid-sql
* Return value according to the type of argument
* Fix codes for abnoraml inputs, updated math-expr.md
* Fix assert text
* Fix error messages and refactor codes
* Fix compile error, update sql.md, refactor codes and format tests
---
.../java/org/apache/druid/math/expr/Function.java | 41 +++++++++++-
docs/content/misc/math-expr.md | 2 +-
docs/content/querying/sql.md | 1 +
.../builtin/RoundOperatorConversion.java | 59 +++++++++++++++++
.../sql/calcite/planner/DruidOperatorTable.java | 2 +
.../sql/calcite/expression/ExpressionsTest.java | 77 ++++++++++++++++++++++
6 files changed, 178 insertions(+), 4 deletions(-)
diff --git a/core/src/main/java/org/apache/druid/math/expr/Function.java
b/core/src/main/java/org/apache/druid/math/expr/Function.java
index 000b893..4c8cd93 100644
--- a/core/src/main/java/org/apache/druid/math/expr/Function.java
+++ b/core/src/main/java/org/apache/druid/math/expr/Function.java
@@ -26,6 +26,8 @@ import org.apache.druid.java.util.common.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.List;
/**
@@ -499,7 +501,7 @@ interface Function
}
}
- class Round extends SingleParamMath
+ class Round implements Function
{
@Override
public String name()
@@ -508,9 +510,42 @@ interface Function
}
@Override
- protected ExprEval eval(double param)
+ public ExprEval apply(List<Expr> args, Expr.ObjectBinding bindings)
{
- return ExprEval.of(Math.round(param));
+ if (args.size() != 1 && args.size() != 2) {
+ throw new IAE("Function[%s] needs 1 or 2 arguments", name());
+ }
+
+ ExprEval value1 = args.get(0).eval(bindings);
+ if (value1.type() != ExprType.LONG && value1.type() != ExprType.DOUBLE) {
+ throw new IAE("The first argument to the function[%s] should be
integer or double type but get the %s type", name(), value1.type());
+ }
+
+ if (args.size() == 1) {
+ return eval(value1);
+ } else {
+ ExprEval value2 = args.get(1).eval(bindings);
+ if (value2.type() != ExprType.LONG) {
+ throw new IAE("The second argument to the function[%s] should be
integer type but get the %s type", name(), value2.type());
+ }
+ return eval(value1, value2.asInt());
+ }
+ }
+
+ private ExprEval eval(ExprEval param)
+ {
+ return eval(param, 0);
+ }
+
+ private ExprEval eval(ExprEval param, int scale)
+ {
+ if (param.type() == ExprType.LONG) {
+ return ExprEval.of(BigDecimal.valueOf(param.asLong()).setScale(scale,
RoundingMode.HALF_UP).longValue());
+ } else if (param.type() == ExprType.DOUBLE) {
+ return
ExprEval.of(BigDecimal.valueOf(param.asDouble()).setScale(scale,
RoundingMode.HALF_UP).doubleValue());
+ } else {
+ return ExprEval.of(null);
+ }
}
}
diff --git a/docs/content/misc/math-expr.md b/docs/content/misc/math-expr.md
index ce3389f..b8b8409 100644
--- a/docs/content/misc/math-expr.md
+++ b/docs/content/misc/math-expr.md
@@ -133,7 +133,7 @@ See javadoc of java.lang.Math for detailed explanation for
each function.
|pow|pow(x, y) would return the value of the x raised to the power of y|
|remainder|remainder(x, y) would return the remainder operation on two
arguments as prescribed by the IEEE 754 standard|
|rint|rint(x) would return value that is closest in value to x and is equal to
a mathematical integer|
-|round|round(x) would return the closest long value to x, with ties rounding
up|
+|round|round(x, y) would return the value of the x rounded to the y decimal
places. While x can be an integer or floating-point number, y must be an
integer. The type of the return value is specified by that of x. y defaults to
0 if omitted. When y is negative, x is rounded on the left side of the y
decimal points.|
|scalb|scalb(d, sf) would return d * 2^sf rounded as if performed by a single
correctly rounded floating-point multiply to a member of the double value set|
|signum|signum(x) would return the signum function of the argument x|
|sin|sin(x) would return the trigonometric sine of an angle x|
diff --git a/docs/content/querying/sql.md b/docs/content/querying/sql.md
index 5e7ef13..d596c14 100644
--- a/docs/content/querying/sql.md
+++ b/docs/content/querying/sql.md
@@ -149,6 +149,7 @@ Numeric functions will return 64 bit integers or 64 bit
floats, depending on the
|`SQRT(expr)`|Square root.|
|`TRUNCATE(expr[, digits])`|Truncate expr to a specific number of decimal
digits. If digits is negative, then this truncates that many places to the left
of the decimal point. Digits defaults to zero if not specified.|
|`TRUNC(expr[, digits])`|Synonym for `TRUNCATE`.|
+|`ROUND(expr[, digits])`|`ROUND(x, y)` would return the value of the x rounded
to the y decimal places. While x can be an integer or floating-point number, y
must be an integer. The type of the return value is specified by that of x. y
defaults to 0 if omitted. When y is negative, x is rounded on the left side of
the y decimal points.|
|`x + y`|Addition.|
|`x - y`|Subtraction.|
|`x * y`|Multiplication.|
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RoundOperatorConversion.java
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RoundOperatorConversion.java
new file mode 100644
index 0000000..558a1b9
--- /dev/null
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/expression/builtin/RoundOperatorConversion.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.druid.sql.calcite.expression.builtin;
+
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.druid.sql.calcite.expression.DruidExpression;
+import org.apache.druid.sql.calcite.expression.OperatorConversions;
+import org.apache.druid.sql.calcite.expression.SqlOperatorConversion;
+import org.apache.druid.sql.calcite.planner.PlannerContext;
+import org.apache.druid.sql.calcite.table.RowSignature;
+
+public class RoundOperatorConversion implements SqlOperatorConversion
+{
+ private static final SqlFunction SQL_FUNCTION = OperatorConversions
+ .operatorBuilder("ROUND")
+ .operandTypes(SqlTypeFamily.NUMERIC, SqlTypeFamily.INTEGER)
+ .requiredOperands(1)
+ .returnTypeInference(ReturnTypes.ARG0)
+ .functionCategory(SqlFunctionCategory.NUMERIC)
+ .build();
+
+ @Override
+ public SqlFunction calciteOperator()
+ {
+ return SQL_FUNCTION;
+ }
+
+ @Override
+ public DruidExpression toDruidExpression(final PlannerContext
plannerContext, final RowSignature rowSignature, final RexNode rexNode)
+ {
+ return OperatorConversions.convertCall(plannerContext, rowSignature,
rexNode, inputExpressions -> {
+ return DruidExpression.fromFunctionCall(
+ "round",
+ inputExpressions
+ );
+ });
+ }
+}
diff --git
a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
index c6759b5..82682f5 100644
---
a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
+++
b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java
@@ -65,6 +65,7 @@ import
org.apache.druid.sql.calcite.expression.builtin.ReinterpretOperatorConver
import
org.apache.druid.sql.calcite.expression.builtin.RepeatOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.ReverseOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.RightOperatorConversion;
+import org.apache.druid.sql.calcite.expression.builtin.RoundOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.StringFormatOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.StrposOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.SubstringOperatorConversion;
@@ -159,6 +160,7 @@ public class DruidOperatorTable implements SqlOperatorTable
.add(new
BinaryOperatorConversion(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, "<="))
.add(new BinaryOperatorConversion(SqlStdOperatorTable.AND, "&&"))
.add(new BinaryOperatorConversion(SqlStdOperatorTable.OR, "||"))
+ .add(new RoundOperatorConversion())
// time operators
.add(new CeilOperatorConversion())
.add(new DateTruncOperatorConversion())
diff --git
a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
index fa890a1..ee6f616 100644
---
a/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
+++
b/sql/src/test/java/org/apache/druid/sql/calcite/expression/ExpressionsTest.java
@@ -48,6 +48,7 @@ import
org.apache.druid.sql.calcite.expression.builtin.RegexpExtractOperatorConv
import
org.apache.druid.sql.calcite.expression.builtin.RepeatOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.ReverseOperatorConversion;
import org.apache.druid.sql.calcite.expression.builtin.RightOperatorConversion;
+import org.apache.druid.sql.calcite.expression.builtin.RoundOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.StringFormatOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.StrposOperatorConversion;
import
org.apache.druid.sql.calcite.expression.builtin.TimeExtractOperatorConversion;
@@ -464,6 +465,82 @@ public class ExpressionsTest extends CalciteTestBase
}
@Test
+ public void testRound()
+ {
+ final SqlFunction roundFunction = new
RoundOperatorConversion().calciteOperator();
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("a")),
+ DruidExpression.fromExpression("round(\"a\")"),
+ 10L
+ );
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("b")),
+ DruidExpression.fromExpression("round(\"b\")"),
+ 25L
+ );
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("b"), integerLiteral(-1)),
+ DruidExpression.fromExpression("round(\"b\",-1)"),
+ 30L
+ );
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("x")),
+ DruidExpression.fromExpression("round(\"x\")"),
+ 2.0
+ );
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("x"), integerLiteral(1)),
+ DruidExpression.fromExpression("round(\"x\",1)"),
+ 2.3
+ );
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("y")),
+ DruidExpression.fromExpression("round(\"y\")"),
+ 3.0
+ );
+
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("z")),
+ DruidExpression.fromExpression("round(\"z\")"),
+ -2.0
+ );
+ }
+
+ @Test
+ public void testRoundWithInvalidArgument()
+ {
+ final SqlFunction roundFunction = new
RoundOperatorConversion().calciteOperator();
+
+ expectedException.expect(IAE.class);
+ expectedException.expectMessage("The first argument to the function[round]
should be integer or double type but get the STRING type");
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("s")),
+ DruidExpression.fromExpression("round(\"s\")"),
+ "IAE Exception"
+ );
+ }
+
+ @Test
+ public void testRoundWithInvalidSecondArgument()
+ {
+ final SqlFunction roundFunction = new
RoundOperatorConversion().calciteOperator();
+
+ expectedException.expect(IAE.class);
+ expectedException.expectMessage("The second argument to the
function[round] should be integer type but get the STRING type");
+ testExpression(
+ rexBuilder.makeCall(roundFunction, inputRef("x"),
rexBuilder.makeLiteral("foo")),
+ DruidExpression.fromExpression("round(\"x\",'foo')"),
+ "IAE Exception"
+ );
+ }
+
+ @Test
public void testDateTrunc()
{
testExpression(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]