From 782fc7c09da25c44ecb61620acf2f7ce9d7077ad Mon Sep 17 00:00:00 2001 From: twalthr Date: Fri, 2 Sep 2016 11:00:09 +0200 Subject: [PATCH] [FLINK-4549] [table] Test and document implicitly supported SQL functions --- docs/dev/table_api.md | 873 ++++++++++++++++-- .../api/table/codegen/CodeGenerator.scala | 26 +- .../table/expressions/SqlExpressionTest.scala | 161 ++++ .../expressions}/TemporalTypesTest.scala | 2 +- 4 files changed, 972 insertions(+), 90 deletions(-) create mode 100644 flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/SqlExpressionTest.scala rename flink-libraries/flink-table/src/test/scala/org/apache/flink/api/{scala/expression => table/expressions}/TemporalTypesTest.scala (99%) diff --git a/docs/dev/table_api.md b/docs/dev/table_api.md index fe3ddc3129ef1..fea317fd2a1b9 100644 --- a/docs/dev/table_api.md +++ b/docs/dev/table_api.md @@ -1212,10 +1212,10 @@ Advanced types such as generic types, composite types (e.g. POJOs or Tuples), an {% top %} -Scalar Functions +Built-in Functions ---------------- -Both the Table API and SQL come with a set of built-in scalar functions for data transformations. This section gives a brief overview of the available scalar function so far. +Both the Table API and SQL come with a set of built-in functions for data transformations. This section gives a brief overview of the available functions so far.
@@ -2035,12 +2035,554 @@ localTimestamp()
+ + + + +The Flink SQL functions (including their syntax) are a subset of Apache Calcite's built-in functions. Most of the documentation has been adopted from the [Calcite SQL reference](https://calcite.apache.org/docs/reference.html). +
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionComparison functionsDescription
+ {% highlight text %} +value1 = value2 +{% endhighlight %} + +

Equals.

+
+ {% highlight text %} +value1 <> value2 +{% endhighlight %} + +

Not equal.

+
+ {% highlight text %} +value1 > value2 +{% endhighlight %} + +

Greater than.

+
+ {% highlight text %} +value1 >= value2 +{% endhighlight %} + +

Greater than or equal.

+
+ {% highlight text %} +value1 < value2 +{% endhighlight %} + +

Less than.

+
+ {% highlight text %} +value1 <= value2 +{% endhighlight %} + +

Less than or equal.

+
+ {% highlight text %} +value IS NULL +{% endhighlight %} + +

Whether value is null.

+
+ {% highlight text %} +value IS NOT NULL +{% endhighlight %} + +

Whether value is not null.

+
+ {% highlight text %} +value1 IS DISTINCT FROM value2 +{% endhighlight %} + +

Whether two values are not equal, treating null values as the same.

+
+ {% highlight text %} +value1 IS NOT DISTINCT FROM value2 +{% endhighlight %} + +

Whether two values are equal, treating null values as the same.

+
+ {% highlight text %} +value1 BETWEEN [ASYMMETRIC | SYMMETRIC] value2 AND value3 +{% endhighlight %} + +

Whether value1 is greater than or equal to value2 and less than or equal to value3.

+
+ {% highlight text %} +value1 NOT BETWEEN value2 AND value3 +{% endhighlight %} + +

Whether value1 is less than value2 or greater than value3.

+
+ {% highlight text %} +string1 LIKE string2 +{% endhighlight %} + +

Whether string1 matches pattern string2.

+
+ {% highlight text %} +string1 NOT LIKE string2 +{% endhighlight %} + +

Whether string1 does not match pattern string2.

+
+ {% highlight text %} +string1 SIMILAR TO string2 +{% endhighlight %} + +

Whether string1 matches regular expression string2.

+
+ {% highlight text %} +string1 NOT SIMILAR TO string2 +{% endhighlight %} + +

Whether string1 does not match regular expression string2.

+
+ {% highlight text %} +value IN (value [, value]* ) +{% endhighlight %} + +

Whether value is equal to a value in a list.

+
+ {% highlight text %} +value NOT IN (value [, value]* ) +{% endhighlight %} + +

Whether value is not equal to every value in a list.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Logical functionsDescription
+ {% highlight text %} +boolean1 OR boolean2 +{% endhighlight %} + +

Whether boolean1 is TRUE or boolean2 is TRUE.

+
+ {% highlight text %} +boolean1 AND boolean2 +{% endhighlight %} + +

Whether boolean1 and boolean2 are both TRUE.

+
+ {% highlight text %} +NOT boolean +{% endhighlight %} + +

Whether boolean is not TRUE; returns UNKNOWN if boolean is UNKNOWN.

+
+ {% highlight text %} +boolean IS FALSE +{% endhighlight %} + +

Whether boolean is FALSE; returns FALSE if boolean is UNKNOWN.

+
+ {% highlight text %} +boolean IS NOT FALSE +{% endhighlight %} + +

Whether boolean is not FALSE; returns TRUE if boolean is UNKNOWN.

+
+ {% highlight text %} +boolean IS TRUE +{% endhighlight %} + +

Whether boolean is TRUE; returns FALSE if boolean is UNKNOWN.

+
+ {% highlight text %} +boolean IS NOT TRUE +{% endhighlight %} + +

Whether boolean is not TRUE; returns TRUE if boolean is UNKNOWN.

+
+ {% highlight text %} +boolean IS UNKNOWN +{% endhighlight %} + +

Whether boolean is UNKNOWN.

+
+ {% highlight text %} +boolean IS NOT UNKNOWN +{% endhighlight %} + +

Whether boolean is not UNKNOWN.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Arithmetic functionsDescription
+ {% highlight text %} ++ numeric +{% endhighlight %} + +

Returns numeric.

+
+ {% highlight text %} +- numeric +{% endhighlight %} + +

Returns negative numeric.

+
+ {% highlight text %} +numeric1 + numeric2 +{% endhighlight %} + +

Returns numeric1 plus numeric2.

+
+ {% highlight text %} +numeric1 - numeric2 +{% endhighlight %} + +

Returns numeric1 minus numeric2.

+
+ {% highlight text %} +numeric1 * numeric2 +{% endhighlight %} + +

Returns numeric1 multiplied by numeric2.

+
+ {% highlight text %} +numeric1 / numeric2 +{% endhighlight %} + +

Returns numeric1 divided by numeric2.

+
+ {% highlight text %} +POWER(numeric1, numeric2) +{% endhighlight %} + +

Returns numeric1 raised to the power of numeric2.

+
+ {% highlight text %} +ABS(numeric) +{% endhighlight %} + +

Returns the absolute value of numeric.

+
+ {% highlight text %} +MOD(numeric1, numeric2) +{% endhighlight %} + +

Returns the remainder (modulus) of numeric1 divided by numeric2. The result is negative only if numeric1 is negative.

+
+ {% highlight text %} +SQRT(numeric) +{% endhighlight %} + +

Returns the square root of numeric.

+
+ {% highlight text %} +LN(numeric) +{% endhighlight %} + +

Returns the natural logarithm (base e) of numeric.

+
+ {% highlight text %} +LOG10(numeric) +{% endhighlight %} + +

Returns the base 10 logarithm of numeric.

+
+ {% highlight text %} +EXP(numeric) +{% endhighlight %} + +

Returns e raised to the power of numeric.

+
+ {% highlight text %} +CEIL(numeric) +{% endhighlight %} + +

Rounds numeric up, and returns the smallest number that is greater than or equal to numeric.

+
+ {% highlight text %} +FLOOR(numeric) +{% endhighlight %} + +

Rounds numeric down, and returns the largest number that is less than or equal to numeric.

+
+ + + + + @@ -2048,256 +2590,303 @@ localTimestamp() - + +
String functions Description
- {% highlight sql %} -EXP(NUMERIC) + {% highlight text %} +string || string {% endhighlight %} -

Calculates the Euler's number raised to the given power.

+

Concatenates two character strings.

- {% highlight sql %} -LOG10(NUMERIC) + {% highlight text %} +CHAR_LENGTH(string) {% endhighlight %} -

Calculates the base 10 logarithm of given value.

+

Returns the number of characters in a character string.

- {% highlight sql %} -LN(NUMERIC) + {% highlight text %} +CHARACTER_LENGTH(string) {% endhighlight %} -

Calculates the natural logarithm of given value.

+

As CHAR_LENGTH(string).

- {% highlight sql %} -POWER(NUMERIC, NUMERIC) + {% highlight text %} +UPPER(string) {% endhighlight %} -

Calculates the given number raised to the power of the other value.

+

Returns a character string converted to upper case.

- {% highlight sql %} -ABS(NUMERIC) + {% highlight text %} +LOWER(string) {% endhighlight %} -

Calculates the absolute value of given value.

+

Returns a character string converted to lower case.

- {% highlight sql %} -FLOOR(NUMERIC) + {% highlight text %} +POSITION(string1 IN string2) {% endhighlight %} -

Calculates the largest integer less than or equal to a given number.

+

Returns the position of the first occurrence of string1 in string2.

- {% highlight sql %} -CEIL(NUMERIC) + {% highlight text %} +TRIM( { BOTH | LEADING | TRAILING } string1 FROM string2) {% endhighlight %} -

Calculates the smallest integer greater than or equal to a given number.

+

Removes leading and/or trailing characters from string2. By default, whitespaces at both sides are removed.

- {% highlight sql %} -SUBSTRING(VARCHAR, INT, INT) -SUBSTRING(VARCHAR FROM INT FOR INT) + {% highlight text %} +OVERLAY(string1 PLACING string2 FROM integer [ FOR integer2 ]) {% endhighlight %} -

Creates a substring of the given string at the given index for the given length. The index starts at 1 and is inclusive, i.e., the character at the index is included in the substring. The substring has the specified length or less.

+

Replaces a substring of string1 with string2.

- {% highlight sql %} -SUBSTRING(VARCHAR, INT) -SUBSTRING(VARCHAR FROM INT) + {% highlight text %} +SUBSTRING(string FROM integer) {% endhighlight %} -

Creates a substring of the given string beginning at the given index to the end. The start index starts at 1 and is inclusive.

+

Returns a substring of a character string starting at a given point.

- {% highlight sql %} -TRIM(LEADING VARCHAR FROM VARCHAR) -TRIM(TRAILING VARCHAR FROM VARCHAR) -TRIM(BOTH VARCHAR FROM VARCHAR) -TRIM(VARCHAR) + {% highlight text %} +SUBSTRING(string FROM integer FOR integer) {% endhighlight %} -

Removes leading and/or trailing characters from the given string. By default, whitespaces at both sides are removed.

+

Returns a substring of a character string starting at a given point with a given length.

- {% highlight sql %} -CHAR_LENGTH(VARCHAR) + {% highlight text %} +INITCAP(string) {% endhighlight %} -

Returns the length of a String.

+

Returns string with the first letter of each word converter to upper case and the rest to lower case. Words are sequences of alphanumeric characters separated by non-alphanumeric characters.

+ + + + + + + + + + - + +
Conditional functionsDescription
- {% highlight sql %} -UPPER(VARCHAR) + {% highlight text %} +CASE value +WHEN value1 [, value11 ]* THEN result1 +[ WHEN valueN [, valueN1 ]* THEN resultN ]* +[ ELSE resultZ ] +END {% endhighlight %} -

Returns all of the characters in a string in upper case using the rules of the default locale.

+

Simple case.

- {% highlight sql %} -LOWER(VARCHAR) + {% highlight text %} +CASE +WHEN condition1 THEN result1 +[ WHEN conditionN THEN resultN ]* +[ ELSE resultZ ] +END {% endhighlight %} -

Returns all of the characters in a string in lower case using the rules of the default locale.

+

Searched case.

- {% highlight sql %} -INITCAP(VARCHAR) + {% highlight text %} +NULLIF(value, value) {% endhighlight %} -

Converts the initial letter of each word in a string to uppercase. Assumes a string containing only [A-Za-z0-9], everything else is treated as whitespace.

+

Returns NULL if the values are the same. For example, NULLIF(5, 5) returns NULL; NULLIF(5, 0) returns 5.

- {% highlight sql %} -VARCHAR LIKE VARCHAR + {% highlight text %} +COALESCE(value, value [, value ]* ) {% endhighlight %} -

Returns true, if a string matches the specified LIKE pattern. E.g. "Jo_n%" matches all strings that start with "Jo(arbitrary letter)n".

+

Provides a value if the first value is null. For example, COALESCE(NULL, 5) returns 5.

+ + + + + + + + + + + +
Type conversion functionsDescription
- {% highlight sql %} -VARCHAR SIMILAR TO VARCHAR + {% highlight text %} +CAST(value AS type) {% endhighlight %} -

Returns true, if a string matches the specified SQL regex pattern. E.g. "A+" matches all strings that consist of at least one "A".

+

Converts a value to a given type.

+ + + + + + + + + + +
Value constructor functionsDescription
- {% highlight sql %} -DATE VARCHAR + {% highlight text %} +ROW (value [, value]* ) {% endhighlight %} -

Parses a date string in the form "yy-mm-dd" to a SQL date.

+

Creates a row from a list of values.

- {% highlight sql %} -TIME VARCHAR + {% highlight text %} +(value [, value]* ) {% endhighlight %} -

Parses a time string in the form "hh:mm:ss" to a SQL time.

+

Creates a row from a list of values.

+ + + + + + + + + @@ -2308,7 +2897,7 @@ CURRENT_DATE @@ -2319,7 +2908,7 @@ CURRENT_TIME @@ -2330,7 +2919,7 @@ CURRENT_TIMESTAMP @@ -2341,7 +2930,7 @@ LOCALTIME @@ -2350,8 +2939,118 @@ LOCALTIMESTAMP + + + + + + + + + + + + + + + +
Temporal functionsDescription
- {% highlight sql %} -TIMESTAMP VARCHAR + {% highlight text %} +DATE string {% endhighlight %} -

Parses a timestamp string in the form "yy-mm-dd hh:mm:ss.fff" to a SQL timestamp.

+

Parses a date string in the form "yy-mm-dd" to a SQL date.

- {% highlight sql %} -EXTRACT(TIMEINTERVALUNIT FROM TEMPORAL) + {% highlight text %} +TIME string {% endhighlight %} -

Extracts parts of a time point or time interval. Returns the part as a long value. E.g. EXTRACT(DAY FROM DATE '2006-06-05') leads to 5.

+

Parses a time string in the form "hh:mm:ss" to a SQL time.

- {% highlight sql %} -FLOOR(TIMEPOINT TO TIMEINTERVALUNIT) + {% highlight text %} +TIMESTAMP string {% endhighlight %} -

Rounds a time point down to the given unit. E.g. FLOOR(TIME '12:44:31' TO MINUTE) leads to 12:44:00.

+

Parses a timestamp string in the form "yy-mm-dd hh:mm:ss.fff" to a SQL timestamp.

- {% highlight sql %} -CEIL(TIMEPOINT TO TIMEINTERVALUNIT) + {% highlight text %} +INTERVAL string range {% endhighlight %} -

Rounds a time point up to the given unit. E.g. CEIL(TIME '12:44:31' TO MINUTE) leads to 12:45:00.

+

Parses an interval string in the form "dd hh:mm:ss.fff" for SQL intervals of milliseconds or "yyyy-mm" for SQL intervals of months. An interval range might be e.g. DAY, MINUTE, DAY TO HOUR, or DAY TO SECOND for intervals of milliseconds; YEAR or YEAR TO MONTH for intervals of months. E.g. INTERVAL '10 00:00:00.004' DAY TO SECOND, INTERVAL '10' DAY, or INTERVAL '2-10' YEAR TO MONTH return intervals.

- {% highlight sql %} + {% highlight text %} CURRENT_DATE {% endhighlight %}
- {% highlight sql %} + {% highlight text %} CURRENT_TIME {% endhighlight %}
- {% highlight sql %} + {% highlight text %} CURRENT_TIMESTAMP {% endhighlight %}
- {% highlight sql %} + {% highlight text %} LOCALTIME {% endhighlight %}
- {% highlight sql %} + {% highlight text %} LOCALTIMESTAMP {% endhighlight %}
+ {% highlight text %} +EXTRACT(timeintervalunit FROM temporal) +{% endhighlight %} + +

Extracts parts of a time point or time interval. Returns the part as a long value. E.g. EXTRACT(DAY FROM DATE '2006-06-05') leads to 5.

+
+ {% highlight text %} +FLOOR(timepoint TO timeintervalunit) +{% endhighlight %} + +

Rounds a time point down to the given unit. E.g. FLOOR(TIME '12:44:31' TO MINUTE) leads to 12:44:00.

+
+ {% highlight text %} +CEIL(timepoint TO timeintervalunit) +{% endhighlight %} + +

Rounds a time point up to the given unit. E.g. CEIL(TIME '12:44:31' TO MINUTE) leads to 12:45:00.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Aggregate functionsDescription
+ {% highlight text %} +COUNT(value [, value]* ) +{% endhighlight %} + +

Returns the number of input rows for which value is not null.

+
+ {% highlight text %} +COUNT(*) +{% endhighlight %} + +

Returns the number of input rows.

+
+ {% highlight text %} +AVG(numeric) +{% endhighlight %} + +

Returns the average (arithmetic mean) of numeric across all input values.

+
+ {% highlight text %} +SUM(numeric) +{% endhighlight %} + +

Returns the sum of numeric across all input values.

+
+ {% highlight text %} +MAX(value) +{% endhighlight %} + +

Returns the maximum value of value across all input values.

+
+ {% highlight text %} +MIN(value) +{% endhighlight %} + +

Returns the minimum value of value across all input values.

+
+
@@ -2442,6 +3141,18 @@ object TimestampModifier extends ScalarFunction { +### Limitations + +Not supported at all yet: + +- Binary string operators and functions +- System functions +- Collection functions +- Aggregate functions like STDDEV_xxx, VAR_xxx, and REGR_xxx +- Distinct aggregate functions like COUNT DISTINCT +- Window functions +- Grouping functions + {% top %} diff --git a/flink-libraries/flink-table/src/main/scala/org/apache/flink/api/table/codegen/CodeGenerator.scala b/flink-libraries/flink-table/src/main/scala/org/apache/flink/api/table/codegen/CodeGenerator.scala index 39ee26cc2e13b..dfac6c8b527a2 100644 --- a/flink-libraries/flink-table/src/main/scala/org/apache/flink/api/table/codegen/CodeGenerator.scala +++ b/flink-libraries/flink-table/src/main/scala/org/apache/flink/api/table/codegen/CodeGenerator.scala @@ -545,7 +545,9 @@ class CodeGenerator( generateInputAccess(input._1, input._2, index) } - override def visitFieldAccess(rexFieldAccess: RexFieldAccess): GeneratedExpression = ??? + override def visitFieldAccess(rexFieldAccess: RexFieldAccess): GeneratedExpression = + throw new CodeGenException("The 'FieldAccess' feature is currently not supported.") + override def visitLiteral(literal: RexLiteral): GeneratedExpression = { val resultType = FlinkTypeFactory.toTypeInfo(literal.getType) @@ -657,13 +659,17 @@ class CodeGenerator( } } - override def visitCorrelVariable(correlVariable: RexCorrelVariable): GeneratedExpression = ??? + override def visitCorrelVariable(correlVariable: RexCorrelVariable): GeneratedExpression = + throw new CodeGenException("The 'CorrelVariable' feature is currently not supported.") - override def visitLocalRef(localRef: RexLocalRef): GeneratedExpression = ??? + override def visitLocalRef(localRef: RexLocalRef): GeneratedExpression = + throw new CodeGenException("The 'LocalRef' feature is currently not supported.") - override def visitRangeRef(rangeRef: RexRangeRef): GeneratedExpression = ??? + override def visitRangeRef(rangeRef: RexRangeRef): GeneratedExpression = + throw new CodeGenException("The 'RangeRef' feature is currently not supported.") - override def visitDynamicParam(dynamicParam: RexDynamicParam): GeneratedExpression = ??? + override def visitDynamicParam(dynamicParam: RexDynamicParam): GeneratedExpression = + throw new CodeGenException("The 'DynamicParam' feature is currently not supported.") override def visitCall(call: RexCall): GeneratedExpression = { val operands = call.getOperands.map(_.accept(this)) @@ -853,7 +859,9 @@ class CodeGenerator( operands.map(_.resultType), resultType) callGen - .getOrElse(throw new CodeGenException(s"Unsupported call: $sqlOperator")) + .getOrElse(throw new CodeGenException(s"Unsupported call: $sqlOperator \n" + + s"If you think this function should be supported, " + + s"you can create an issue and start a discussion for it.")) .generate(this, operands) // unknown or invalid @@ -862,9 +870,11 @@ class CodeGenerator( } } - override def visitOver(over: RexOver): GeneratedExpression = ??? + override def visitOver(over: RexOver): GeneratedExpression = + throw new CodeGenException("The 'Over' feature is currently not supported.") - override def visitSubQuery(subQuery: RexSubQuery): GeneratedExpression = ??? + override def visitSubQuery(subQuery: RexSubQuery): GeneratedExpression = + throw new CodeGenException("The 'SubQuery' feature is currently not supported.") // ---------------------------------------------------------------------------------------------- // generator helping methods diff --git a/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/SqlExpressionTest.scala b/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/SqlExpressionTest.scala new file mode 100644 index 0000000000000..cae438818ade0 --- /dev/null +++ b/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/SqlExpressionTest.scala @@ -0,0 +1,161 @@ +/* + * 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.api.table.expressions + +import org.apache.flink.api.common.typeinfo.TypeInformation +import org.apache.flink.api.table.Row +import org.apache.flink.api.table.expressions.utils.ExpressionTestBase +import org.apache.flink.api.table.typeutils.RowTypeInfo +import org.junit.Test + +/** + * Tests all SQL expressions that are currently supported according to the documentation. + * This tests should be kept in sync with the documentation to reduce confusion due to the + * large amount of SQL functions. + * + * The tests do not test every parameter combination of a function. + * They are rather a function existence test and simple functional test. + * + * The tests are split up and ordered like the sections in the documentation. + */ +class SqlExpressionTest extends ExpressionTestBase { + + @Test + def testComparisonFunctions(): Unit = { + testSqlApi("1 = 1", "true") + testSqlApi("1 <> 1", "false") + testSqlApi("5 > 2", "true") + testSqlApi("2 >= 2", "true") + testSqlApi("5 < 2", "false") + testSqlApi("2 <= 2", "true") + testSqlApi("1 IS NULL", "false") + testSqlApi("1 IS NOT NULL", "true") + testSqlApi("NULLIF(1,1) IS DISTINCT FROM NULLIF(1,1)", "false") + testSqlApi("NULLIF(1,1) IS NOT DISTINCT FROM NULLIF(1,1)", "true") + testSqlApi("NULLIF(1,1) IS NOT DISTINCT FROM NULLIF(1,1)", "true") + testSqlApi("12 BETWEEN 11 AND 13", "true") + testSqlApi("12 BETWEEN ASYMMETRIC 13 AND 11", "false") + testSqlApi("12 BETWEEN SYMMETRIC 13 AND 11", "true") + testSqlApi("12 NOT BETWEEN 11 AND 13", "false") + testSqlApi("12 NOT BETWEEN ASYMMETRIC 13 AND 11", "true") + testSqlApi("12 NOT BETWEEN SYMMETRIC 13 AND 11", "false") + testSqlApi("'TEST' LIKE '%EST'", "true") + //testSqlApi("'%EST' LIKE '.%EST' ESCAPE '.'", "true") // TODO + testSqlApi("'TEST' NOT LIKE '%EST'", "false") + //testSqlApi("'%EST' NOT LIKE '.%EST' ESCAPE '.'", "false") // TODO + testSqlApi("'TEST' SIMILAR TO '.EST'", "true") + //testSqlApi("'TEST' SIMILAR TO ':.EST' ESCAPE ':'", "true") // TODO + testSqlApi("'TEST' NOT SIMILAR TO '.EST'", "false") + //testSqlApi("'TEST' NOT SIMILAR TO ':.EST' ESCAPE ':'", "false") // TODO + testSqlApi("'TEST' IN ('west', 'TEST', 'rest')", "true") + testSqlApi("'TEST' IN ('west', 'rest')", "false") + testSqlApi("'TEST' NOT IN ('west', 'TEST', 'rest')", "false") + testSqlApi("'TEST' NOT IN ('west', 'rest')", "true") + + // sub-query functions are not listed here + } + + @Test + def testLogicalFunctions(): Unit = { + testSqlApi("TRUE OR FALSE", "true") + testSqlApi("TRUE AND FALSE", "false") + testSqlApi("NOT TRUE", "false") + testSqlApi("TRUE IS FALSE", "false") + testSqlApi("TRUE IS NOT FALSE", "true") + testSqlApi("TRUE IS TRUE", "true") + testSqlApi("TRUE IS NOT TRUE", "false") + testSqlApi("NULLIF(TRUE,TRUE) IS UNKNOWN", "true") + testSqlApi("NULLIF(TRUE,TRUE) IS NOT UNKNOWN", "false") + } + + @Test + def testArithmeticFunctions(): Unit = { + testSqlApi("+5", "5") + testSqlApi("-5", "-5") + testSqlApi("5+5", "10") + testSqlApi("5-5", "0") + testSqlApi("5*5", "25") + testSqlApi("5/5", "1") + testSqlApi("POWER(5, 5)", "3125.0") + testSqlApi("ABS(-5)", "5") + testSqlApi("MOD(-26, 5)", "-1") + testSqlApi("SQRT(4)", "2.0") + testSqlApi("LN(1)", "0.0") + testSqlApi("LOG10(1)", "0.0") + testSqlApi("EXP(0)", "1.0") + testSqlApi("CEIL(2.5)", "3") + testSqlApi("FLOOR(2.5)", "2") + } + + @Test + def testStringFunctions(): Unit = { + testSqlApi("'test' || 'string'", "teststring") + testSqlApi("CHAR_LENGTH('string')", "6") + testSqlApi("CHARACTER_LENGTH('string')", "6") + testSqlApi("UPPER('string')", "STRING") + testSqlApi("LOWER('STRING')", "string") + testSqlApi("POSITION('STR' IN 'STRING')", "1") + testSqlApi("TRIM(BOTH ' STRING ')", "STRING") + testSqlApi("TRIM(LEADING 'x' FROM 'xxxxSTRINGxxxx')", "STRINGxxxx") + testSqlApi("TRIM(TRAILING 'x' FROM 'xxxxSTRINGxxxx')", "xxxxSTRING") + testSqlApi( + "OVERLAY('This is a old string' PLACING 'new' FROM 11 FOR 3)", + "This is a new string") + testSqlApi("SUBSTRING('hello world', 2)", "ello world") + testSqlApi("SUBSTRING('hello world', 2, 3)", "ell") + testSqlApi("INITCAP('hello world')", "Hello World") + } + + @Test + def testConditionalFunctions(): Unit = { + testSqlApi("CASE 2 WHEN 1, 2 THEN 2 ELSE 3 END", "2") + testSqlApi("CASE WHEN 1 = 2 THEN 2 WHEN 1 = 1 THEN 3 ELSE 3 END", "3") + testSqlApi("NULLIF(1, 1)", "null") + testSqlApi("COALESCE(NULL, 5)", "5") + } + + @Test + def testTypeConversionFunctions(): Unit = { + testSqlApi("CAST(2 AS DOUBLE)", "2.0") + } + + @Test + def testValueConstructorFunctions(): Unit = { + testSqlApi("ROW('hello world', 12)", "hello world") // test base only returns field 0 + testSqlApi("('hello world', 12)", "hello world") // test base only returns field 0 + } + + @Test + def testDateTimeFunctions(): Unit = { + testSqlApi("DATE '1990-10-14'", "1990-10-14") + testSqlApi("TIME '12:12:12'", "12:12:12") + testSqlApi("TIMESTAMP '1990-10-14 12:12:12.123'", "1990-10-14 12:12:12.123") + testSqlApi("INTERVAL '10 00:00:00.004' DAY TO SECOND", "+10 00:00:00.004") + testSqlApi("INTERVAL '10 00:12' DAY TO MINUTE", "+10 00:12:00.000") + testSqlApi("INTERVAL '2-10' YEAR TO MONTH", "+2-10") + testSqlApi("EXTRACT(DAY FROM DATE '1990-12-01')", "1") + testSqlApi("EXTRACT(DAY FROM INTERVAL '19 12:10:10.123' DAY TO SECOND(3))", "19") + testSqlApi("QUARTER(DATE '2016-04-12')", "2") + } + + override def testData: Any = new Row(0) + + override def typeInfo: TypeInformation[Any] = + new RowTypeInfo(Seq()).asInstanceOf[TypeInformation[Any]] +} diff --git a/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/scala/expression/TemporalTypesTest.scala b/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala similarity index 99% rename from flink-libraries/flink-table/src/test/scala/org/apache/flink/api/scala/expression/TemporalTypesTest.scala rename to flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala index 63d634614c6c7..24ff51ccbaf9e 100644 --- a/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/scala/expression/TemporalTypesTest.scala +++ b/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.flink.api.scala.expression +package org.apache.flink.api.table.expressions import java.sql.{Date, Time, Timestamp}