[FLINK-6112] [table] Support Calcite 1.12's new numerical functions This closes #3718.
Project: http://git-wip-us.apache.org/repos/asf/flink/repo Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/a06b3222 Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/a06b3222 Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/a06b3222 Branch: refs/heads/table-retraction Commit: a06b3222bb97150f643c22fe1a1054e3239c1c2d Parents: d27d3dd Author: mtunique <oatg...@gmail.com> Authored: Fri Apr 7 18:04:03 2017 +0800 Committer: twalthr <twal...@apache.org> Committed: Sun Apr 30 15:12:46 2017 +0200 ---------------------------------------------------------------------- docs/dev/table_api.md | 396 ++++++++++++++++++ .../flink/table/api/scala/expressionDsl.scala | 68 ++++ .../table/codegen/calls/BuiltInMethods.scala | 46 +++ .../table/codegen/calls/ConstantCallGen.scala | 38 ++ .../table/codegen/calls/FunctionGenerator.scala | 161 ++++++++ .../table/expressions/ExpressionParser.scala | 11 +- .../table/expressions/mathExpressions.scala | 158 ++++++++ .../flink/table/validate/FunctionCatalog.scala | 24 ++ .../table/expressions/ScalarFunctionsTest.scala | 402 ++++++++++++++++++- .../table/expressions/SqlExpressionTest.scala | 12 + 10 files changed, 1312 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/docs/dev/table_api.md ---------------------------------------------------------------------- diff --git a/docs/dev/table_api.md b/docs/dev/table_api.md index 4b9e565..6d52a78 100644 --- a/docs/dev/table_api.md +++ b/docs/dev/table_api.md @@ -1977,6 +1977,127 @@ BOOLEAN.isNotFalse </td> </tr> + <tr> + <td> + {% highlight java %} +NUMERIC.sin() +{% endhighlight %} + </td> + <td> + <p>Calculates the sine of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.cos() +{% endhighlight %} + </td> + <td> + <p>Calculates the cosine of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.tan() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.cot() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.asin() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.acos() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.atan() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.degrees() +{% endhighlight %} + </td> + <td> + <p>Converts <i>numeric</i> from radians to degrees.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.radians() +{% endhighlight %} + </td> + <td> + <p>Converts <i>numeric</i> from degrees to radians.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.sign() +{% endhighlight %} + </td> + <td> + <p>Calculates the signum of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight java %} +NUMERIC.round(numeric2) +{% endhighlight %} + </td> + <td> + <p>Rounds the given number to the other value places right to the decimal point.</p> + </td> + </tr> + </tbody> </table> @@ -2156,6 +2277,17 @@ NUMERIC.floor() </td> </tr> + <tr> + <td> + {% highlight java %} +pi() +{% endhighlight %} + </td> + <td> + <p> Returns pi.</p> + </td> + </tr> + </tbody> </table> @@ -3175,6 +3307,138 @@ NUMERIC.floor() <p>Calculates the largest integer less than or equal to a given number.</p> </td> </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.sin() +{% endhighlight %} + </td> + <td> + <p>Calculates the sine of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.cos() +{% endhighlight %} + </td> + <td> + <p>Calculates the cosine of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.tan() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.cot() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.asin() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.acos() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.atan() +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.degrees() +{% endhighlight %} + </td> + <td> + <p>Converts <i>numeric</i> from radians to degrees.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.radians() +{% endhighlight %} + </td> + <td> + <p>Converts <i>numeric</i> from degrees to radians.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.sign() +{% endhighlight %} + </td> + <td> + <p>Calculates the signum of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +NUMERIC.round(numeric2) +{% endhighlight %} + </td> + <td> + <p>Rounds the given number to the other value places right to the decimal point.</p> + </td> + </tr> + + <tr> + <td> + {% highlight scala %} +pi() +{% endhighlight %} + </td> + <td> + <p> Returns pi.</p> + </td> + </tr> </tbody> </table> @@ -4172,6 +4436,138 @@ boolean IS NOT UNKNOWN </td> </tr> + <tr> + <td> + {% highlight text %} +SIN(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the sine of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +COS(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the cosine of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +TAN(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +COT(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +ASIN(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +ACOS(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +ATAN(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the tangent of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +DEGREES(numeric) +{% endhighlight %} + </td> + <td> + <p>Converts <i>numeric</i> from radians to degrees.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +RADIANS(numeric) +{% endhighlight %} + </td> + <td> + <p>Converts <i>numeric</i> from degrees to radians.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +SIGN(numeric) +{% endhighlight %} + </td> + <td> + <p>Calculates the signum of a given number.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +ROUND(numeric1, numeric2) +{% endhighlight %} + </td> + <td> + <p>Rounds the given number to the other value places right to the decimal point.</p> + </td> + </tr> + + <tr> + <td> + {% highlight text %} +PI +{% endhighlight %} + </td> + <td> + <p> Returns pi.</p> + </td> + </tr> + </tbody> </table> http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/api/scala/expressionDsl.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/api/scala/expressionDsl.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/api/scala/expressionDsl.scala index fc6cb10..169a729 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/api/scala/expressionDsl.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/api/scala/expressionDsl.scala @@ -275,6 +275,61 @@ trait ImplicitExpressionOperations { */ def ceil() = Ceil(expr) + /** + * Calculates the sine of a given number. + */ + def sin() = Sin(expr) + + /** + * Calculates the cosine of a given number. + */ + def cos() = Cos(expr) + + /** + * Calculates the tangent of a given number. + */ + def tan() = Tan(expr) + + /** + * Calculates the tangent of a given number. + */ + def cot() = Cot(expr) + + /** + * Calculates the tangent of a given number. + */ + def asin() = Asin(expr) + + /** + * Calculates the tangent of a given number. + */ + def acos() = Acos(expr) + + /** + * Calculates the tangent of a given number. + */ + def atan() = Atan(expr) + + /** + * Converts numeric from radians to degrees. + */ + def degrees() = Degrees(expr) + + /** + * Converts numeric from degrees to radians. + */ + def radians() = Radians(expr) + + /** + * Calculates the signum of a given number. + */ + def sign() = Sign(expr) + + /** + * Rounds the given number to the other value places right to the decimal point. + */ + def round(other: Expression) = Round(expr, other) + // String operations /** @@ -807,4 +862,17 @@ object array { } } +/** + * Returns pi. + */ +object pi { + + /** + * Returns pi. + */ + def apply(): Expression = { + Pi() + } +} + // scalastyle:on object.name http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/BuiltInMethods.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/BuiltInMethods.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/BuiltInMethods.scala index 649d3b2..dd4be46 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/BuiltInMethods.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/BuiltInMethods.scala @@ -17,6 +17,7 @@ */ package org.apache.flink.table.codegen.calls +import java.lang.{Long => JLong} import java.math.{BigDecimal => JBigDecimal} import org.apache.calcite.linq4j.tree.Types @@ -25,15 +26,60 @@ import org.apache.flink.table.functions.utils.MathFunctions object BuiltInMethods { val LOG10 = Types.lookupMethod(classOf[Math], "log10", classOf[Double]) + val EXP = Types.lookupMethod(classOf[Math], "exp", classOf[Double]) + val POWER = Types.lookupMethod(classOf[Math], "pow", classOf[Double], classOf[Double]) val POWER_DEC = Types.lookupMethod( classOf[MathFunctions], "power", classOf[Double], classOf[JBigDecimal]) + val LN = Types.lookupMethod(classOf[Math], "log", classOf[Double]) + val ABS = Types.lookupMethod(classOf[SqlFunctions], "abs", classOf[Double]) val ABS_DEC = Types.lookupMethod(classOf[SqlFunctions], "abs", classOf[JBigDecimal]) + val LIKE_WITH_ESCAPE = Types.lookupMethod(classOf[SqlFunctions], "like", classOf[String], classOf[String], classOf[String]) + val SIMILAR_WITH_ESCAPE = Types.lookupMethod(classOf[SqlFunctions], "similar", classOf[String], classOf[String], classOf[String]) + + val SIN = Types.lookupMethod(classOf[Math], "sin", classOf[Double]) + val SIN_DEC = Types.lookupMethod(classOf[SqlFunctions], "sin", classOf[JBigDecimal]) + + val COS = Types.lookupMethod(classOf[Math], "cos", classOf[Double]) + val COS_DEC = Types.lookupMethod(classOf[SqlFunctions], "cos", classOf[JBigDecimal]) + + val TAN = Types.lookupMethod(classOf[Math], "tan", classOf[Double]) + val TAN_DEC = Types.lookupMethod(classOf[SqlFunctions], "tan", classOf[JBigDecimal]) + + val COT = Types.lookupMethod(classOf[SqlFunctions], "cot", classOf[Double]) + val COT_DEC = Types.lookupMethod(classOf[SqlFunctions], "cot", classOf[JBigDecimal]) + + val ASIN = Types.lookupMethod(classOf[Math], "asin", classOf[Double]) + val ASIN_DEC = Types.lookupMethod(classOf[SqlFunctions], "asin", classOf[JBigDecimal]) + + val ACOS = Types.lookupMethod(classOf[Math], "acos", classOf[Double]) + val ACOS_DEC = Types.lookupMethod(classOf[SqlFunctions], "acos", classOf[JBigDecimal]) + + val ATAN = Types.lookupMethod(classOf[Math], "atan", classOf[Double]) + val ATAN_DEC = Types.lookupMethod(classOf[SqlFunctions], "atan", classOf[JBigDecimal]) + + val DEGREES = Types.lookupMethod(classOf[Math], "toDegrees", classOf[Double]) + val DEGREES_DEC = Types.lookupMethod(classOf[SqlFunctions], "degrees", classOf[JBigDecimal]) + + val RADIANS = Types.lookupMethod(classOf[Math], "toRadians", classOf[Double]) + val RADIANS_DEC = Types.lookupMethod(classOf[SqlFunctions], "radians", classOf[JBigDecimal]) + + val SIGN_DOUBLE = Types.lookupMethod(classOf[Math], "signum", classOf[Double]) + val SIGN_INT = Types.lookupMethod(classOf[Integer], "signum", classOf[Int]) + val SIGN_LONG = Types.lookupMethod(classOf[JLong], "signum", classOf[Long]) + val SIGN_DEC = Types.lookupMethod(classOf[SqlFunctions], "sign", classOf[JBigDecimal]) + + val ROUND_DOUBLE = Types.lookupMethod(classOf[SqlFunctions], "sround", classOf[Double], + classOf[Int]) + val ROUND_INT = Types.lookupMethod(classOf[SqlFunctions], "sround", classOf[Int], classOf[Int]) + val ROUND_LONG = Types.lookupMethod(classOf[SqlFunctions], "sround", classOf[Long], classOf[Int]) + val ROUND_DEC = Types.lookupMethod(classOf[SqlFunctions], "sround", classOf[JBigDecimal], + classOf[Int]) } http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/ConstantCallGen.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/ConstantCallGen.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/ConstantCallGen.scala new file mode 100644 index 0000000..39fb13b --- /dev/null +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/ConstantCallGen.scala @@ -0,0 +1,38 @@ +/* + * 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.flink.table.codegen.calls + +import org.apache.flink.api.common.typeinfo.TypeInformation +import org.apache.flink.table.codegen.{CodeGenerator, GeneratedExpression} + +/** + * Generates a function call which return a constant. + */ +class ConstantCallGen( + targetType: TypeInformation[_], + constantCode: String) extends CallGenerator { + + override def generate( + codeGenerator: CodeGenerator, + operands: Seq[GeneratedExpression]) + : GeneratedExpression = { + codeGenerator.generateNonNullLiteral(targetType, constantCode) + } + +} http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/FunctionGenerator.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/FunctionGenerator.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/FunctionGenerator.scala index 27e6dc6..b8b001f 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/FunctionGenerator.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/codegen/calls/FunctionGenerator.scala @@ -179,6 +179,167 @@ object FunctionGenerator { DOUBLE_TYPE_INFO, BuiltInMethods.POWER_DEC) + addSqlFunctionMethod( + SIN, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.SIN) + + addSqlFunctionMethod( + SIN, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.SIN_DEC) + + addSqlFunctionMethod( + COS, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.COS) + + addSqlFunctionMethod( + COS, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.COS_DEC) + + addSqlFunctionMethod( + TAN, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.TAN) + + addSqlFunctionMethod( + TAN, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.TAN_DEC) + + addSqlFunctionMethod( + COT, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.COT) + + addSqlFunctionMethod( + COT, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.COT_DEC) + + addSqlFunctionMethod( + ASIN, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ASIN) + + addSqlFunctionMethod( + ASIN, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ASIN_DEC) + + addSqlFunctionMethod( + ACOS, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ACOS) + + addSqlFunctionMethod( + ACOS, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ACOS_DEC) + + addSqlFunctionMethod( + ATAN, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ATAN) + + addSqlFunctionMethod( + ATAN, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ATAN_DEC) + + addSqlFunctionMethod( + DEGREES, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.DEGREES) + + addSqlFunctionMethod( + DEGREES, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.DEGREES_DEC) + + addSqlFunctionMethod( + RADIANS, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.RADIANS) + + addSqlFunctionMethod( + RADIANS, + Seq(BIG_DEC_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.RADIANS_DEC) + + addSqlFunction( + PI, + Seq(), + new ConstantCallGen(DOUBLE_TYPE_INFO, Math.PI.toString)) + + addSqlFunctionMethod( + SIGN, + Seq(DOUBLE_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.SIGN_DOUBLE) + + addSqlFunctionMethod( + SIGN, + Seq(INT_TYPE_INFO), + INT_TYPE_INFO, + BuiltInMethods.SIGN_INT) + + addSqlFunctionMethod( + SIGN, + Seq(LONG_TYPE_INFO), + LONG_TYPE_INFO, + BuiltInMethods.SIGN_LONG) + + addSqlFunctionMethod( + SIGN, + Seq(BIG_DEC_TYPE_INFO), + BIG_DEC_TYPE_INFO, + BuiltInMethods.SIGN_DEC) + + addSqlFunctionMethod( + ROUND, + Seq(LONG_TYPE_INFO, INT_TYPE_INFO), + LONG_TYPE_INFO, + BuiltInMethods.ROUND_LONG) + + addSqlFunctionMethod( + ROUND, + Seq(INT_TYPE_INFO, INT_TYPE_INFO), + INT_TYPE_INFO, + BuiltInMethods.ROUND_INT) + + addSqlFunctionMethod( + ROUND, + Seq(BIG_DEC_TYPE_INFO, INT_TYPE_INFO), + BIG_DEC_TYPE_INFO, + BuiltInMethods.ROUND_DEC) + + addSqlFunctionMethod( + ROUND, + Seq(DOUBLE_TYPE_INFO, INT_TYPE_INFO), + DOUBLE_TYPE_INFO, + BuiltInMethods.ROUND_DOUBLE) + addSqlFunction( ABS, Seq(DOUBLE_TYPE_INFO), http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/ExpressionParser.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/ExpressionParser.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/ExpressionParser.scala index 1985248..113b85a 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/ExpressionParser.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/ExpressionParser.scala @@ -92,6 +92,7 @@ object ExpressionParser extends JavaTokenParsers with PackratParsers { lazy val CURRENT_RANGE: Keyword = Keyword("current_range") lazy val UNBOUNDED_ROW: Keyword = Keyword("unbounded_row") lazy val UNBOUNDED_RANGE: Keyword = Keyword("unbounded_range") + lazy val ASIN: Keyword = Keyword("asin") def functionIdent: ExpressionParser.Parser[String] = not(ARRAY) ~ not(AS) ~ not(COUNT) ~ not(AVG) ~ not(MIN) ~ not(MAX) ~ @@ -315,11 +316,14 @@ object ExpressionParser extends JavaTokenParsers with PackratParsers { lazy val suffixAgg: PackratParser[Expression] = suffixSum | suffixMin | suffixMax | suffixCount | suffixAvg + lazy val suffixAsin: PackratParser[Expression] = + composite <~ "." ~ ASIN ~ opt("()") ^^ { e => Asin(e) } + lazy val suffixed: PackratParser[Expression] = suffixTimeInterval | suffixRowInterval | suffixStart | suffixEnd | suffixAgg | suffixCast | suffixAs | suffixTrim | suffixTrimWithoutArgs | suffixIf | suffixAsc | suffixDesc | suffixToDate | suffixToTimestamp | suffixToTime | suffixExtract | - suffixFloor | suffixCeil | suffixGet | suffixFlattening | + suffixFloor | suffixCeil | suffixGet | suffixFlattening | suffixAsin | suffixFunctionCall | suffixFunctionCallOneArg // function call must always be at the end // prefix operators @@ -403,10 +407,13 @@ object ExpressionParser extends JavaTokenParsers with PackratParsers { lazy val prefixAgg: PackratParser[Expression] = prefixSum | prefixMin | prefixMax | prefixCount | prefixAvg + lazy val prefixAsin: PackratParser[Expression] = + ASIN ~ "(" ~> composite <~ ")" ^^ { e => Asin(e) } + lazy val prefixed: PackratParser[Expression] = prefixArray | prefixAgg | prefixStart | prefixEnd | prefixCast | prefixAs | prefixTrim | prefixTrimWithoutArgs | prefixIf | prefixExtract | prefixFloor | prefixCeil | prefixGet | - prefixFlattening | prefixFunctionCall | + prefixFlattening | prefixAsin | prefixFunctionCall | prefixFunctionCallOneArg // function call must always be at the end // over http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/mathExpressions.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/mathExpressions.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/mathExpressions.scala index b31367c..16cb34a 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/mathExpressions.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/expressions/mathExpressions.scala @@ -127,3 +127,161 @@ case class Sqrt(child: Expression) extends UnaryExpression with InputTypeSpec { relBuilder.call(SqlStdOperatorTable.POWER, child.toRexNode, Literal(0.5).toRexNode) } } + +case class Sin(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Sin") + + override def toString: String = s"sin($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.SIN, child.toRexNode) + } +} + +case class Cos(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Cos") + + override def toString: String = s"cos($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.COS, child.toRexNode) + } +} + +case class Tan(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Tan") + + override def toString: String = s"tan($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.TAN, child.toRexNode) + } +} + +case class Cot(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Cot") + + override def toString: String = s"cot($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.COT, child.toRexNode) + } +} + +case class Asin(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Asin") + + override def toString: String = s"asin($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.ASIN, child.toRexNode) + } +} + +case class Acos(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Acos") + + override def toString: String = s"acos($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.ACOS, child.toRexNode) + } +} + +case class Atan(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Atan") + + override def toString: String = s"atan($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.ATAN, child.toRexNode) + } +} + +case class Degrees(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Degrees") + + override def toString: String = s"degrees($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.DEGREES, child.toRexNode) + } +} + +case class Radians(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "Radians") + + override def toString: String = s"radians($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.DEGREES, child.toRexNode) + } +} + +case class Pi() extends LeafExpression { + override private[flink] def resultType: TypeInformation[_] = DOUBLE_TYPE_INFO + + override def toString: String = s"pi()" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.PI) + } +} + +case class Sign(child: Expression) extends UnaryExpression { + override private[flink] def resultType: TypeInformation[_] = child.resultType + + override private[flink] def validateInput(): ValidationResult = + TypeCheckUtils.assertNumericExpr(child.resultType, "sign") + + override def toString: String = s"sign($child)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.SIGN, child.toRexNode) + } +} + +case class Round(left: Expression, right: Expression) extends BinaryExpression { + override private[flink] def resultType: TypeInformation[_] = left.resultType + + override private[flink] def validateInput(): ValidationResult = { + if (!TypeCheckUtils.isInteger(right.resultType)) { + ValidationFailure(s"round right requires int, get " + + s"$right : ${right.resultType}") + } + TypeCheckUtils.assertNumericExpr(left.resultType, s"round left :$left") + } + + override def toString: String = s"round($left, $right)" + + override private[flink] def toRexNode(implicit relBuilder: RelBuilder): RexNode = { + relBuilder.call(SqlStdOperatorTable.ROUND, left.toRexNode, right.toRexNode) + } +} http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/validate/FunctionCatalog.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/validate/FunctionCatalog.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/validate/FunctionCatalog.scala index 74b371a..97a7e96 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/validate/FunctionCatalog.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/table/validate/FunctionCatalog.scala @@ -201,6 +201,18 @@ object FunctionCatalog { "mod" -> classOf[Mod], "sqrt" -> classOf[Sqrt], "minusPrefix" -> classOf[UnaryMinus], + "sin" -> classOf[Sin], + "cos" -> classOf[Cos], + "tan" -> classOf[Tan], + "cot" -> classOf[Cot], + "asin" -> classOf[Asin], + "acos" -> classOf[Acos], + "atan" -> classOf[Atan], + "degrees" -> classOf[Degrees], + "radians" -> classOf[Radians], + "pi" -> classOf[Pi], + "sign" -> classOf[Sign], + "round" -> classOf[Round], // temporal functions "extract" -> classOf[Extract], @@ -348,6 +360,18 @@ class BasicOperatorTable extends ReflectiveSqlOperatorTable { SqlStdOperatorTable.QUARTER, SqlStdOperatorTable.SCALAR_QUERY, SqlStdOperatorTable.EXISTS, + SqlStdOperatorTable.SIN, + SqlStdOperatorTable.COS, + SqlStdOperatorTable.TAN, + SqlStdOperatorTable.COT, + SqlStdOperatorTable.ASIN, + SqlStdOperatorTable.ACOS, + SqlStdOperatorTable.ATAN, + SqlStdOperatorTable.DEGREES, + SqlStdOperatorTable.RADIANS, + SqlStdOperatorTable.PI, + SqlStdOperatorTable.SIGN, + SqlStdOperatorTable.ROUND, // EXTENSIONS EventTimeExtractor, ProcTimeExtractor, http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/ScalarFunctionsTest.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/ScalarFunctionsTest.scala b/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/ScalarFunctionsTest.scala index 474043a..85804bc 100644 --- a/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/ScalarFunctionsTest.scala +++ b/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/ScalarFunctionsTest.scala @@ -696,6 +696,388 @@ class ScalarFunctionsTest extends ExpressionTestBase { "-1231") } + @Test + def testSin(): Unit = { + testAllApis( + 'f2.sin(), + "f2.sin()", + "SIN(f2)", + math.sin(42.toByte).toString) + + testAllApis( + 'f3.sin(), + "f3.sin()", + "SIN(f3)", + math.sin(43.toShort).toString) + + testAllApis( + 'f4.sin(), + "f4.sin()", + "SIN(f4)", + math.sin(44.toLong).toString) + + testAllApis( + 'f5.sin(), + "f5.sin()", + "SIN(f5)", + math.sin(4.5.toFloat).toString) + + testAllApis( + 'f6.sin(), + "f6.sin()", + "SIN(f6)", + math.sin(4.6).toString) + + testAllApis( + 'f15.sin(), + "sin(f15)", + "SIN(f15)", + math.sin(-1231.1231231321321321111).toString) + } + + @Test + def testCos(): Unit = { + testAllApis( + 'f2.cos(), + "f2.cos()", + "COS(f2)", + math.cos(42.toByte).toString) + + testAllApis( + 'f3.cos(), + "f3.cos()", + "COS(f3)", + math.cos(43.toShort).toString) + + testAllApis( + 'f4.cos(), + "f4.cos()", + "COS(f4)", + math.cos(44.toLong).toString) + + testAllApis( + 'f5.cos(), + "f5.cos()", + "COS(f5)", + math.cos(4.5.toFloat).toString) + + testAllApis( + 'f6.cos(), + "f6.cos()", + "COS(f6)", + math.cos(4.6).toString) + + testAllApis( + 'f15.cos(), + "cos(f15)", + "COS(f15)", + math.cos(-1231.1231231321321321111).toString) + } + + @Test + def testTan(): Unit = { + testAllApis( + 'f2.tan(), + "f2.tan()", + "TAN(f2)", + math.tan(42.toByte).toString) + + testAllApis( + 'f3.tan(), + "f3.tan()", + "TAN(f3)", + math.tan(43.toShort).toString) + + testAllApis( + 'f4.tan(), + "f4.tan()", + "TAN(f4)", + math.tan(44.toLong).toString) + + testAllApis( + 'f5.tan(), + "f5.tan()", + "TAN(f5)", + math.tan(4.5.toFloat).toString) + + testAllApis( + 'f6.tan(), + "f6.tan()", + "TAN(f6)", + math.tan(4.6).toString) + + testAllApis( + 'f15.tan(), + "tan(f15)", + "TAN(f15)", + math.tan(-1231.1231231321321321111).toString) + } + + @Test + def testCot(): Unit = { + testAllApis( + 'f2.cot(), + "f2.cot()", + "COT(f2)", + (1.0d / math.tan(42.toByte)).toString) + + testAllApis( + 'f3.cot(), + "f3.cot()", + "COT(f3)", + (1.0d / math.tan(43.toShort)).toString) + + testAllApis( + 'f4.cot(), + "f4.cot()", + "COT(f4)", + (1.0d / math.tan(44.toLong)).toString) + + testAllApis( + 'f5.cot(), + "f5.cot()", + "COT(f5)", + (1.0d / math.tan(4.5.toFloat)).toString) + + testAllApis( + 'f6.cot(), + "f6.cot()", + "COT(f6)", + (1.0d / math.tan(4.6)).toString) + + testAllApis( + 'f15.cot(), + "cot(f15)", + "COT(f15)", + (1.0d / math.tan(-1231.1231231321321321111)).toString) + } + + @Test + def testAsin(): Unit = { + testAllApis( + 'f25.asin(), + "f25.asin()", + "ASIN(f25)", + math.asin(0.42.toByte).toString) + + testAllApis( + 'f26.asin(), + "f26.asin()", + "ASIN(f26)", + math.asin(0.toShort).toString) + + testAllApis( + 'f27.asin(), + "f27.asin()", + "ASIN(f27)", + math.asin(0.toLong).toString) + + testAllApis( + 'f28.asin(), + "f28.asin()", + "ASIN(f28)", + math.asin(0.45.toFloat).toString) + + testAllApis( + 'f29.asin(), + "f29.asin()", + "ASIN(f29)", + math.asin(0.46).toString) + + testAllApis( + 'f30.asin(), + "f30.asin()", + "ASIN(f30)", + math.asin(1).toString) + + testAllApis( + 'f31.asin(), + "f31.asin()", + "ASIN(f31)", + math.asin(-0.1231231321321321111).toString) + } + + @Test + def testAcos(): Unit = { + testAllApis( + 'f25.acos(), + "f25.acos()", + "ACOS(f25)", + math.acos(0.42.toByte).toString) + + testAllApis( + 'f26.acos(), + "f26.acos()", + "ACOS(f26)", + math.acos(0.toShort).toString) + + testAllApis( + 'f27.acos(), + "f27.acos()", + "ACOS(f27)", + math.acos(0.toLong).toString) + + testAllApis( + 'f28.acos(), + "f28.acos()", + "ACOS(f28)", + math.acos(0.45.toFloat).toString) + + testAllApis( + 'f29.acos(), + "f29.acos()", + "ACOS(f29)", + math.acos(0.46).toString) + + testAllApis( + 'f30.acos(), + "f30.acos()", + "ACOS(f30)", + math.acos(1).toString) + + testAllApis( + 'f31.acos(), + "f31.acos()", + "ACOS(f31)", + math.acos(-0.1231231321321321111).toString) + } + + @Test + def testAtan(): Unit = { + testAllApis( + 'f25.atan(), + "f25.atan()", + "ATAN(f25)", + math.atan(0.42.toByte).toString) + + testAllApis( + 'f26.atan(), + "f26.atan()", + "ATAN(f26)", + math.atan(0.toShort).toString) + + testAllApis( + 'f27.atan(), + "f27.atan()", + "ATAN(f27)", + math.atan(0.toLong).toString) + + testAllApis( + 'f28.atan(), + "f28.atan()", + "ATAN(f28)", + math.atan(0.45.toFloat).toString) + + testAllApis( + 'f29.atan(), + "f29.atan()", + "ATAN(f29)", + math.atan(0.46).toString) + + testAllApis( + 'f30.atan(), + "f30.atan()", + "ATAN(f30)", + math.atan(1).toString) + + testAllApis( + 'f31.atan(), + "f31.atan()", + "ATAN(f31)", + math.atan(-0.1231231321321321111).toString) + } + + @Test + def testDegrees(): Unit = { + testAllApis( + 'f2.degrees(), + "f2.degrees()", + "DEGREES(f2)", + math.toDegrees(42.toByte).toString) + + testAllApis( + 'f3.degrees(), + "f3.degrees()", + "DEGREES(f3)", + math.toDegrees(43.toShort).toString) + + testAllApis( + 'f4.degrees(), + "f4.degrees()", + "DEGREES(f4)", + math.toDegrees(44.toLong).toString) + + testAllApis( + 'f5.degrees(), + "f5.degrees()", + "DEGREES(f5)", + math.toDegrees(4.5.toFloat).toString) + + testAllApis( + 'f6.degrees(), + "f6.degrees()", + "DEGREES(f6)", + math.toDegrees(4.6).toString) + + testAllApis( + 'f15.degrees(), + "degrees(f15)", + "DEGREES(f15)", + math.toDegrees(-1231.1231231321321321111).toString) + } + + @Test + def testPi(): Unit = { + testAllApis( + pi(), + "pi()", + "PI", + math.Pi.toString) + } + + @Test + def testSign(): Unit = { + testAllApis( + 'f4.sign(), + "f4.sign()", + "SIGN(f4)", + 1.toString) + + testAllApis( + 'f6.sign(), + "f6.sign()", + "SIGN(f6)", + 1.0.toString) + + testAllApis( + 'f15.sign(), + "sign(f15)", + "SIGN(f15)", + (-1).toString) + } + + @Test + def testRound(): Unit = { + testAllApis( + 'f29.round('f30), + "f29.round(f30)", + "ROUND(f29, f30)", + 0.5.toString) + + testAllApis( + 'f31.round('f7), + "f31.round(f7)", + "ROUND(f31, f7)", + (-0.123).toString) + + testAllApis( + 'f4.round('f32), + "f4.round(f32)", + "ROUND(f4, f32)", + 40.toString) + + } + // ---------------------------------------------------------------------------------------------- // Temporal functions // ---------------------------------------------------------------------------------------------- @@ -1103,7 +1485,7 @@ class ScalarFunctionsTest extends ExpressionTestBase { // ---------------------------------------------------------------------------------------------- def testData = { - val testData = new Row(25) + val testData = new Row(33) testData.setField(0, "This is a test String.") testData.setField(1, true) testData.setField(2, 42.toByte) @@ -1129,6 +1511,14 @@ class ScalarFunctionsTest extends ExpressionTestBase { testData.setField(22, BigDecimal("2").bigDecimal) testData.setField(23, "%This is a test String.") testData.setField(24, "*_This is a test String.") + testData.setField(25, 0.42.toByte) + testData.setField(26, 0.toShort) + testData.setField(27, 0.toLong) + testData.setField(28, 0.45.toFloat) + testData.setField(29, 0.46) + testData.setField(30, 1) + testData.setField(31, BigDecimal("-0.1231231321321321111").bigDecimal) + testData.setField(32, -1) testData } @@ -1158,7 +1548,15 @@ class ScalarFunctionsTest extends ExpressionTestBase { Types.BOOLEAN, Types.DECIMAL, Types.STRING, - Types.STRING).asInstanceOf[TypeInformation[Any]] + Types.STRING, + Types.BYTE, + Types.SHORT, + Types.LONG, + Types.FLOAT, + Types.DOUBLE, + Types.INT, + Types.DECIMAL, + Types.INT).asInstanceOf[TypeInformation[Any]] } } http://git-wip-us.apache.org/repos/asf/flink/blob/a06b3222/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/SqlExpressionTest.scala ---------------------------------------------------------------------- diff --git a/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/SqlExpressionTest.scala b/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/SqlExpressionTest.scala index 76e95ff..b11e291 100644 --- a/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/SqlExpressionTest.scala +++ b/flink-libraries/flink-table/src/test/scala/org/apache/flink/table/expressions/SqlExpressionTest.scala @@ -100,7 +100,19 @@ class SqlExpressionTest extends ExpressionTestBase { testSqlApi("LOG10(1)", "0.0") testSqlApi("EXP(0)", "1.0") testSqlApi("CEIL(2.5)", "3") + testSqlApi("CEILING(2.5)", "3") testSqlApi("FLOOR(2.5)", "2") + testSqlApi("SIN(2.5)", "0.5984721441039564") + testSqlApi("COS(2.5)", "-0.8011436155469337") + testSqlApi("TAN(2.5)", "-0.7470222972386603") + testSqlApi("COT(2.5)", "-1.3386481283041514") + testSqlApi("ASIN(0.5)", "0.5235987755982989") + testSqlApi("ACOS(0.5)", "1.0471975511965979") + testSqlApi("ATAN(0.5)", "0.4636476090008061") + testSqlApi("DEGREES(0.5)", "28.64788975654116") + testSqlApi("PI", "3.141592653589793") + testSqlApi("SIGN(-1.1)", "-1") + testSqlApi("ROUND(-12.345, 2)", "-12.35") } @Test