Repository: flink
Updated Branches:
  refs/heads/master ecbccd940 -> 9a1bc021a


[FLINK-4549] [table] Test and document implicitly supported SQL functions

This closes #2500.


Project: http://git-wip-us.apache.org/repos/asf/flink/repo
Commit: http://git-wip-us.apache.org/repos/asf/flink/commit/9a1bc021
Tree: http://git-wip-us.apache.org/repos/asf/flink/tree/9a1bc021
Diff: http://git-wip-us.apache.org/repos/asf/flink/diff/9a1bc021

Branch: refs/heads/master
Commit: 9a1bc021aed0a3eec8c6eabb843d15b8c2b0b43f
Parents: ecbccd9
Author: twalthr <twal...@apache.org>
Authored: Fri Sep 2 11:00:09 2016 +0200
Committer: twalthr <twal...@apache.org>
Committed: Fri Sep 23 17:25:06 2016 +0200

----------------------------------------------------------------------
 docs/dev/table_api.md                           | 873 +++++++++++++++++--
 .../flink/api/table/codegen/CodeGenerator.scala |  26 +-
 .../scala/expression/TemporalTypesTest.scala    | 389 ---------
 .../table/expressions/SqlExpressionTest.scala   | 161 ++++
 .../table/expressions/TemporalTypesTest.scala   | 389 +++++++++
 5 files changed, 1360 insertions(+), 478 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/flink/blob/9a1bc021/docs/dev/table_api.md
----------------------------------------------------------------------
diff --git a/docs/dev/table_api.md b/docs/dev/table_api.md
index 72b88a6..bb083ff 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.
 
 <div class="codetabs" markdown="1">
 <div data-lang="java" markdown="1">
@@ -2057,12 +2057,554 @@ temporalOverlaps(TIMEPOINT, TEMPORAL, TIMEPOINT, 
TEMPORAL)
 </div>
 
 <div data-lang="SQL" markdown="1">
+
+
+<!--
+This list of SQL functions should be kept in sync with SqlExpressionTest to 
reduce confusion due to the large amount of SQL functions.
+The documentation is split up and ordered like the tests in SqlExpressionTest.
+-->
+
+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).
+
 <br />
 
 <table class="table table-bordered">
   <thead>
     <tr>
-      <th class="text-left" style="width: 40%">Function</th>
+      <th class="text-left" style="width: 40%">Comparison functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr>
+      <td>
+        {% highlight text %}
+value1 = value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Equals.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 <> value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Not equal.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 > value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Greater than.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 >= value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Greater than or equal.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 < value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Less than.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 <= value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Less than or equal.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value IS NULL
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value</i> is null.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value IS NOT NULL
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value</i> is not null.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 IS DISTINCT FROM value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether two values are not equal, treating null values as the 
same.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 IS NOT DISTINCT FROM value2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether two values are equal, treating null values as the same.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 BETWEEN [ASYMMETRIC | SYMMETRIC] value2 AND value3
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value1</i> is greater than or equal to <i>value2</i> and 
less than or equal to <i>value3</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value1 NOT BETWEEN value2 AND value3
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value1</i> is less than <i>value2</i> or greater than 
<i>value3</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+string1 LIKE string2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>string1</i> matches pattern <i>string2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+string1 NOT LIKE string2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>string1</i> does not match pattern <i>string2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+string1 SIMILAR TO string2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>string1</i> matches regular expression 
<i>string2</i>.</p>
+      </td>
+    </tr>
+
+
+    <tr>
+      <td>
+        {% highlight text %}
+string1 NOT SIMILAR TO string2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>string1</i> does not match regular expression 
<i>string2</i>.</p>
+      </td>
+    </tr>
+
+
+    <tr>
+      <td>
+        {% highlight text %}
+value IN (value [, value]* )
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value</i> is equal to a value in a list.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value NOT IN (value [, value]* )
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value</i> is not equal to every value in a list.</p>
+      </td>
+    </tr>
+<!-- NOT SUPPORTED SO FAR
+    <tr>
+      <td>
+        {% highlight text %}
+value IN (sub-query)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value</i> is equal to a row returned by sub-query.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+value NOT IN (sub-query)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>value</i> is not equal to every row returned by 
sub-query.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+EXISTS (sub-query)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether sub-query returns at least one row.</p>
+      </td>
+    </tr>-->
+
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Logical functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr>
+      <td>
+        {% highlight text %}
+boolean1 OR boolean2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean1</i> is TRUE or <i>boolean2</i> is TRUE.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean1 AND boolean2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean1</i> and <i>boolean2</i> are both TRUE.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+NOT boolean
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is not TRUE; returns UNKNOWN if 
<i>boolean</i> is UNKNOWN.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean IS FALSE
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is FALSE; returns FALSE if <i>boolean</i> is 
UNKNOWN.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean IS NOT FALSE
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is not FALSE; returns TRUE if <i>boolean</i> 
is UNKNOWN.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean IS TRUE
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is TRUE; returns FALSE if <i>boolean</i> is 
UNKNOWN.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean IS NOT TRUE
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is not TRUE; returns TRUE if <i>boolean</i> 
is UNKNOWN.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean IS UNKNOWN
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is UNKNOWN.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+boolean IS NOT UNKNOWN
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Whether <i>boolean</i> is not UNKNOWN.</p>
+      </td>
+    </tr>
+
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Arithmetic functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr>
+      <td>
+        {% highlight text %}
++ numeric
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns <i>numeric</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+- numeric
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns negative <i>numeric</i>.</p>
+      </td>
+    </tr>
+    
+    <tr>
+      <td>
+        {% highlight text %}
+numeric1 + numeric2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns <i>numeric1</i> plus <i>numeric2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+numeric1 - numeric2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns <i>numeric1</i> minus <i>numeric2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+numeric1 * numeric2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns <i>numeric1</i> multiplied by <i>numeric2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+numeric1 / numeric2
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns <i>numeric1</i> divided by <i>numeric2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+POWER(numeric1, numeric2)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns <i>numeric1</i> raised to the power of <i>numeric2</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+ABS(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the absolute value of <i>numeric</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+MOD(numeric1, numeric2)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the remainder (modulus) of <i>numeric1</i> divided by 
<i>numeric2</i>. The result is negative only if <i>numeric1</i> is negative.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+SQRT(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the square root of <i>numeric</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+LN(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the natural logarithm (base e) of <i>numeric</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+LOG10(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the base 10 logarithm of <i>numeric</i>.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+EXP(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns e raised to the power of <i>numeric</i>.</p>
+      </td>
+    </tr>   
+
+    <tr>
+      <td>
+        {% highlight text %}
+CEIL(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Rounds <i>numeric</i> up, and returns the smallest number that is 
greater than or equal to <i>numeric</i>.</p>
+      </td>
+    </tr>  
+
+    <tr>
+      <td>
+        {% highlight text %}
+FLOOR(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Rounds <i>numeric</i> down, and returns the largest number that is 
less than or equal to <i>numeric</i>.</p>
+      </td>
+    </tr>
+
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">String functions</th>
       <th class="text-center">Description</th>
     </tr>
   </thead>
@@ -2070,256 +2612,303 @@ temporalOverlaps(TIMEPOINT, TEMPORAL, TIMEPOINT, 
TEMPORAL)
   <tbody>
     <tr>
       <td>
-        {% highlight sql %}
-EXP(NUMERIC)
+        {% highlight text %}
+string || string
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the Euler's number raised to the given power.</p>
+        <p>Concatenates two character strings.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-LOG10(NUMERIC)
+        {% highlight text %}
+CHAR_LENGTH(string)
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the base 10 logarithm of given value.</p>
+        <p>Returns the number of characters in a character string.</p>
       </td>
     </tr>
 
-
     <tr>
       <td>
-        {% highlight sql %}
-LN(NUMERIC)
+        {% highlight text %}
+CHARACTER_LENGTH(string)
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the natural logarithm of given value.</p>
+        <p>As CHAR_LENGTH(<i>string</i>).</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-POWER(NUMERIC, NUMERIC)
+        {% highlight text %}
+UPPER(string)
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the given number raised to the power of the other 
value.</p>
+        <p>Returns a character string converted to upper case.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-ABS(NUMERIC)
+        {% highlight text %}
+LOWER(string)
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the absolute value of given value.</p>
+        <p>Returns a character string converted to lower case.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-FLOOR(NUMERIC)
+        {% highlight text %}
+POSITION(string1 IN string2)
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the largest integer less than or equal to a given 
number.</p>
+        <p>Returns the position of the first occurrence of <i>string1</i> in 
<i>string2</i>.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-CEIL(NUMERIC)
+        {% highlight text %}
+TRIM( { BOTH | LEADING | TRAILING } string1 FROM string2)
 {% endhighlight %}
       </td>
       <td>
-        <p>Calculates the smallest integer greater than or equal to a given 
number.</p>
+        <p>Removes leading and/or trailing characters from <i>string2</i>. By 
default, whitespaces at both sides are removed.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-SUBSTRING(VARCHAR, INT, INT)
-SUBSTRING(VARCHAR FROM INT FOR INT)
+        {% highlight text %}
+OVERLAY(string1 PLACING string2 FROM integer [ FOR integer2 ])
 {% endhighlight %}
       </td>
       <td>
-        <p>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.</p>
+        <p>Replaces a substring of <i>string1</i> with <i>string2</i>.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-SUBSTRING(VARCHAR, INT)
-SUBSTRING(VARCHAR FROM INT)
+        {% highlight text %}
+SUBSTRING(string FROM integer)
 {% endhighlight %}
       </td>
       <td>
-        <p>Creates a substring of the given string beginning at the given 
index to the end. The start index starts at 1 and is inclusive.</p>
+        <p>Returns a substring of a character string starting at a given 
point.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% 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 %}
       </td>
       <td>
-        <p>Removes leading and/or trailing characters from the given string. 
By default, whitespaces at both sides are removed.</p>
+        <p>Returns a substring of a character string starting at a given point 
with a given length.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-CHAR_LENGTH(VARCHAR)
+        {% highlight text %}
+INITCAP(string)
 {% endhighlight %}
       </td>
       <td>
-        <p>Returns the length of a String.</p>
+        <p>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.</p>
       </td>
     </tr>
 
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Conditional functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
+
+  <tbody>
     <tr>
       <td>
-        {% highlight sql %}
-UPPER(VARCHAR)
+        {% highlight text %}
+CASE value
+WHEN value1 [, value11 ]* THEN result1
+[ WHEN valueN [, valueN1 ]* THEN resultN ]*
+[ ELSE resultZ ]
+END
 {% endhighlight %}
       </td>
       <td>
-        <p>Returns all of the characters in a string in upper case using the 
rules of the default locale.</p>
+        <p>Simple case.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-LOWER(VARCHAR)
+        {% highlight text %}
+CASE
+WHEN condition1 THEN result1
+[ WHEN conditionN THEN resultN ]*
+[ ELSE resultZ ]
+END
 {% endhighlight %}
       </td>
       <td>
-        <p>Returns all of the characters in a string in lower case using the 
rules of the default locale.</p>
+        <p>Searched case.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-INITCAP(VARCHAR)
+        {% highlight text %}
+NULLIF(value, value)
 {% endhighlight %}
       </td>
-
       <td>
-        <p>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.</p>
+        <p>Returns NULL if the values are the same. For example, 
<code>NULLIF(5, 5)</code> returns NULL; <code>NULLIF(5, 0)</code> returns 5.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-VARCHAR LIKE VARCHAR
+        {% highlight text %}
+COALESCE(value, value [, value ]* )
 {% endhighlight %}
       </td>
       <td>
-        <p>Returns true, if a string matches the specified LIKE pattern. E.g. 
"Jo_n%" matches all strings that start with "Jo(arbitrary letter)n".</p>
+        <p>Provides a value if the first value is null. For example, 
<code>COALESCE(NULL, 5)</code> returns 5.</p>
       </td>
     </tr>
 
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Type conversion functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
+
+  <tbody>
     <tr>
       <td>
-        {% highlight sql %}
-VARCHAR SIMILAR TO VARCHAR
+        {% highlight text %}
+CAST(value AS type)
 {% endhighlight %}
       </td>
       <td>
-        <p>Returns true, if a string matches the specified SQL regex pattern. 
E.g. "A+" matches all strings that consist of at least one "A".</p>
+        <p>Converts a value to a given type.</p>
       </td>
     </tr>
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Value constructor functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
 
+  <tbody>
     <tr>
       <td>
-        {% highlight sql %}
-DATE VARCHAR
+        {% highlight text %}
+ROW (value [, value]* )
 {% endhighlight %}
       </td>
       <td>
-        <p>Parses a date string in the form "yy-mm-dd" to a SQL date.</p>
+        <p>Creates a row from a list of values.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-TIME VARCHAR
+        {% highlight text %}
+(value [, value]* )
 {% endhighlight %}
       </td>
       <td>
-        <p>Parses a time string in the form "hh:mm:ss" to a SQL time.</p>
+        <p>Creates a row from a list of values.</p>
       </td>
     </tr>
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Temporal functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
 
+  <tbody>
     <tr>
       <td>
-        {% highlight sql %}
-TIMESTAMP VARCHAR
+        {% highlight text %}
+DATE string
 {% endhighlight %}
       </td>
       <td>
-        <p>Parses a timestamp string in the form "yy-mm-dd hh:mm:ss.fff" to a 
SQL timestamp.</p>
+        <p>Parses a date string in the form "yy-mm-dd" to a SQL date.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-EXTRACT(TIMEINTERVALUNIT FROM TEMPORAL)
+        {% highlight text %}
+TIME string
 {% endhighlight %}
       </td>
       <td>
-        <p>Extracts parts of a time point or time interval. Returns the part 
as a long value. E.g. <code>EXTRACT(DAY FROM DATE '2006-06-05')</code> leads to 
5.</p>
+        <p>Parses a time <i>string</i> in the form "hh:mm:ss" to a SQL 
time.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-FLOOR(TIMEPOINT TO TIMEINTERVALUNIT)
+        {% highlight text %}
+TIMESTAMP string
 {% endhighlight %}
       </td>
       <td>
-        <p>Rounds a time point down to the given unit. E.g. <code>FLOOR(TIME 
'12:44:31' TO MINUTE)</code> leads to 12:44:00.</p>
+        <p>Parses a timestamp <i>string</i> in the form "yy-mm-dd 
hh:mm:ss.fff" to a SQL timestamp.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
-CEIL(TIMEPOINT TO TIMEINTERVALUNIT)
+        {% highlight text %}
+INTERVAL string range
 {% endhighlight %}
       </td>
       <td>
-        <p>Rounds a time point up to the given unit. E.g. <code>CEIL(TIME 
'12:44:31' TO MINUTE)</code> leads to 12:45:00.</p>
+        <p>Parses an interval <i>string</i> 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. <code>DAY</code>, <code>MINUTE</code>, <code>DAY 
TO HOUR</code>, or <code>DAY TO SECOND</code> for intervals of milliseconds; 
<code>YEAR</code> or <code>YEAR TO MONTH</code> for intervals of months. E.g. 
<code>INTERVAL '10 00:00:00.004' DAY TO SECOND</code>, <code>INTERVAL '10' 
DAY</code>, or <code>INTERVAL '2-10' YEAR TO MONTH</code> return intervals.</p>
       </td>
     </tr>
 
     <tr>
       <td>
-        {% highlight sql %}
+        {% highlight text %}
 CURRENT_DATE
 {% endhighlight %}
       </td>
@@ -2330,7 +2919,7 @@ CURRENT_DATE
 
     <tr>
       <td>
-        {% highlight sql %}
+        {% highlight text %}
 CURRENT_TIME
 {% endhighlight %}
       </td>
@@ -2341,7 +2930,7 @@ CURRENT_TIME
 
     <tr>
       <td>
-        {% highlight sql %}
+        {% highlight text %}
 CURRENT_TIMESTAMP
 {% endhighlight %}
       </td>
@@ -2352,7 +2941,7 @@ CURRENT_TIMESTAMP
 
     <tr>
       <td>
-        {% highlight sql %}
+        {% highlight text %}
 LOCALTIME
 {% endhighlight %}
       </td>
@@ -2363,7 +2952,7 @@ LOCALTIME
 
     <tr>
       <td>
-        {% highlight sql %}
+        {% highlight text %}
 LOCALTIMESTAMP
 {% endhighlight %}
       </td>
@@ -2372,8 +2961,118 @@ LOCALTIMESTAMP
       </td>
     </tr>
 
+    <tr>
+      <td>
+        {% highlight text %}
+EXTRACT(timeintervalunit FROM temporal)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Extracts parts of a time point or time interval. Returns the part 
as a long value. E.g. <code>EXTRACT(DAY FROM DATE '2006-06-05')</code> leads to 
5.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+FLOOR(timepoint TO timeintervalunit)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Rounds a time point down to the given unit. E.g. <code>FLOOR(TIME 
'12:44:31' TO MINUTE)</code> leads to 12:44:00.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+CEIL(timepoint TO timeintervalunit)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Rounds a time point up to the given unit. E.g. <code>CEIL(TIME 
'12:44:31' TO MINUTE)</code> leads to 12:45:00.</p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+<table class="table table-bordered">
+  <thead>
+    <tr>
+      <th class="text-left" style="width: 40%">Aggregate functions</th>
+      <th class="text-center">Description</th>
+    </tr>
+  </thead>
+
+  <tbody>
+    <tr>
+      <td>
+        {% highlight text %}
+COUNT(value [, value]* )
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the number of input rows for which <i>value</i> is not 
null.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+COUNT(*)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the number of input rows.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+AVG(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the average (arithmetic mean) of <i>numeric</i> across all 
input values.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+SUM(numeric)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the sum of <i>numeric</i> across all input values.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+MAX(value)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the maximum value of <i>value</i> across all input 
values.</p>
+      </td>
+    </tr>
+
+    <tr>
+      <td>
+        {% highlight text %}
+MIN(value)
+{% endhighlight %}
+      </td>
+      <td>
+        <p>Returns the minimum value of <i>value</i> across all input 
values.</p>
+      </td>
+    </tr>
   </tbody>
 </table>
+
 </div>
 </div>
 
@@ -2464,6 +3163,18 @@ object TimestampModifier extends ScalarFunction {
 </div>
 </div>
 
+### Limitations
+
+The following operations are not supported 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 %}

http://git-wip-us.apache.org/repos/asf/flink/blob/9a1bc021/flink-libraries/flink-table/src/main/scala/org/apache/flink/api/table/codegen/CodeGenerator.scala
----------------------------------------------------------------------
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 39ee26c..e5a07b1 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("Accesses to fields are not supported yet.")
+
 
   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("Correlating variables are not supported yet.")
 
-  override def visitLocalRef(localRef: RexLocalRef): GeneratedExpression = ???
+  override def visitLocalRef(localRef: RexLocalRef): GeneratedExpression =
+    throw new CodeGenException("Local variables are not supported yet.")
 
-  override def visitRangeRef(rangeRef: RexRangeRef): GeneratedExpression = ???
+  override def visitRangeRef(rangeRef: RexRangeRef): GeneratedExpression =
+    throw new CodeGenException("Range references are not supported yet.")
 
-  override def visitDynamicParam(dynamicParam: RexDynamicParam): 
GeneratedExpression = ???
+  override def visitDynamicParam(dynamicParam: RexDynamicParam): 
GeneratedExpression =
+    throw new CodeGenException("Dynamic parameter references are not supported 
yet.")
 
   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("Aggregate functions over windows are not 
supported yet.")
 
-  override def visitSubQuery(subQuery: RexSubQuery): GeneratedExpression = ???
+  override def visitSubQuery(subQuery: RexSubQuery): GeneratedExpression =
+    throw new CodeGenException("Subqueries are not supported yet.")
 
   // 
----------------------------------------------------------------------------------------------
   // generator helping methods

http://git-wip-us.apache.org/repos/asf/flink/blob/9a1bc021/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/scala/expression/TemporalTypesTest.scala
----------------------------------------------------------------------
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/scala/expression/TemporalTypesTest.scala
deleted file mode 100644
index 63d6346..0000000
--- 
a/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/scala/expression/TemporalTypesTest.scala
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * 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.scala.expression
-
-import java.sql.{Date, Time, Timestamp}
-
-import org.apache.flink.api.common.typeinfo.TypeInformation
-import org.apache.flink.api.scala.table._
-import org.apache.flink.api.table.expressions.utils.ExpressionTestBase
-import org.apache.flink.api.table.typeutils.RowTypeInfo
-import org.apache.flink.api.table.{Row, Types}
-import org.junit.Test
-
-class TemporalTypesTest extends ExpressionTestBase {
-
-  @Test
-  def testTimePointLiterals(): Unit = {
-    testAllApis(
-      "1990-10-14".toDate,
-      "'1990-10-14'.toDate",
-      "DATE '1990-10-14'",
-      "1990-10-14")
-
-    testTableApi(
-      Date.valueOf("2040-09-11"),
-      "'2040-09-11'.toDate",
-      "2040-09-11")
-
-    testAllApis(
-      "1500-04-30".cast(Types.DATE),
-      "'1500-04-30'.cast(DATE)",
-      "CAST('1500-04-30' AS DATE)",
-      "1500-04-30")
-
-    testAllApis(
-      "15:45:59".toTime,
-      "'15:45:59'.toTime",
-      "TIME '15:45:59'",
-      "15:45:59")
-
-    testTableApi(
-      Time.valueOf("00:00:00"),
-      "'00:00:00'.toTime",
-      "00:00:00")
-
-    testAllApis(
-      "1:30:00".cast(Types.TIME),
-      "'1:30:00'.cast(TIME)",
-      "CAST('1:30:00' AS TIME)",
-      "01:30:00")
-
-    testAllApis(
-      "1990-10-14 23:00:00.123".toTimestamp,
-      "'1990-10-14 23:00:00.123'.toTimestamp",
-      "TIMESTAMP '1990-10-14 23:00:00.123'",
-      "1990-10-14 23:00:00.123")
-
-    testTableApi(
-      Timestamp.valueOf("2040-09-11 00:00:00.000"),
-      "'2040-09-11 00:00:00.000'.toTimestamp",
-      "2040-09-11 00:00:00.0")
-
-    testAllApis(
-      "1500-04-30 12:00:00".cast(Types.TIMESTAMP),
-      "'1500-04-30 12:00:00'.cast(TIMESTAMP)",
-      "CAST('1500-04-30 12:00:00' AS TIMESTAMP)",
-      "1500-04-30 12:00:00.0")
-  }
-
-  @Test
-  def testTimeIntervalLiterals(): Unit = {
-    testAllApis(
-      1.year,
-      "1.year",
-      "INTERVAL '1' YEAR",
-      "+1-00")
-
-    testAllApis(
-      1.month,
-      "1.month",
-      "INTERVAL '1' MONTH",
-      "+0-01")
-
-    testAllApis(
-      12.day,
-      "12.day",
-      "INTERVAL '12' DAY",
-      "+12 00:00:00.000")
-
-    testAllApis(
-      1.hour,
-      "1.hour",
-      "INTERVAL '1' HOUR",
-      "+0 01:00:00.000")
-
-    testAllApis(
-      3.minute,
-      "3.minute",
-      "INTERVAL '3' MINUTE",
-      "+0 00:03:00.000")
-
-    testAllApis(
-      3.second,
-      "3.second",
-      "INTERVAL '3' SECOND",
-      "+0 00:00:03.000")
-
-    testAllApis(
-      3.milli,
-      "3.milli",
-      "INTERVAL '0.003' SECOND",
-      "+0 00:00:00.003")
-  }
-
-  @Test
-  def testTimePointInput(): Unit = {
-    testAllApis(
-      'f0,
-      "f0",
-      "f0",
-      "1990-10-14")
-
-    testAllApis(
-      'f1,
-      "f1",
-      "f1",
-      "10:20:45")
-
-    testAllApis(
-      'f2,
-      "f2",
-      "f2",
-      "1990-10-14 10:20:45.123")
-  }
-
-  @Test
-  def testTimeIntervalInput(): Unit = {
-    testAllApis(
-      'f9,
-      "f9",
-      "f9",
-      "+2-00")
-
-    testAllApis(
-      'f10,
-      "f10",
-      "f10",
-      "+0 00:00:12.000")
-  }
-
-  @Test
-  def testTimePointCasting(): Unit = {
-    testAllApis(
-      'f0.cast(Types.TIMESTAMP),
-      "f0.cast(TIMESTAMP)",
-      "CAST(f0 AS TIMESTAMP)",
-      "1990-10-14 00:00:00.0")
-
-    testAllApis(
-      'f1.cast(Types.TIMESTAMP),
-      "f1.cast(TIMESTAMP)",
-      "CAST(f1 AS TIMESTAMP)",
-      "1970-01-01 10:20:45.0")
-
-    testAllApis(
-      'f2.cast(Types.DATE),
-      "f2.cast(DATE)",
-      "CAST(f2 AS DATE)",
-      "1990-10-14")
-
-    testAllApis(
-      'f2.cast(Types.TIME),
-      "f2.cast(TIME)",
-      "CAST(f2 AS TIME)",
-      "10:20:45")
-
-    testAllApis(
-      'f2.cast(Types.TIME),
-      "f2.cast(TIME)",
-      "CAST(f2 AS TIME)",
-      "10:20:45")
-
-    testTableApi(
-      'f7.cast(Types.DATE),
-      "f7.cast(DATE)",
-      "2002-11-09")
-
-    testTableApi(
-      'f7.cast(Types.DATE).cast(Types.INT),
-      "f7.cast(DATE).cast(INT)",
-      "12000")
-
-    testTableApi(
-      'f7.cast(Types.TIME),
-      "f7.cast(TIME)",
-      "00:00:12")
-
-    testTableApi(
-      'f7.cast(Types.TIME).cast(Types.INT),
-      "f7.cast(TIME).cast(INT)",
-      "12000")
-
-    testTableApi(
-      'f8.cast(Types.TIMESTAMP),
-      "f8.cast(TIMESTAMP)",
-      "2016-06-27 07:23:33.0")
-
-    testTableApi(
-      'f8.cast(Types.TIMESTAMP).cast(Types.LONG),
-      "f8.cast(TIMESTAMP).cast(LONG)",
-      "1467012213000")
-  }
-
-  @Test
-  def testTimeIntervalCasting(): Unit = {
-    testTableApi(
-      'f7.cast(Types.INTERVAL_MONTHS),
-      "f7.cast(INTERVAL_MONTHS)",
-      "+1000-00")
-
-    testTableApi(
-      'f8.cast(Types.INTERVAL_MILLIS),
-      "f8.cast(INTERVAL_MILLIS)",
-      "+16979 07:23:33.000")
-  }
-
-  @Test
-  def testTimePointComparison(): Unit = {
-    testAllApis(
-      'f0 < 'f3,
-      "f0 < f3",
-      "f0 < f3",
-      "false")
-
-    testAllApis(
-      'f0 < 'f4,
-      "f0 < f4",
-      "f0 < f4",
-      "true")
-
-    testAllApis(
-      'f1 < 'f5,
-      "f1 < f5",
-      "f1 < f5",
-      "false")
-
-    testAllApis(
-      'f0.cast(Types.TIMESTAMP) !== 'f2,
-      "f0.cast(TIMESTAMP) !== f2",
-      "CAST(f0 AS TIMESTAMP) <> f2",
-      "true")
-
-    testAllApis(
-      'f0.cast(Types.TIMESTAMP) === 'f6,
-      "f0.cast(TIMESTAMP) === f6",
-      "CAST(f0 AS TIMESTAMP) = f6",
-      "true")
-  }
-
-  @Test
-  def testTimeIntervalArithmetic(): Unit = {
-    testAllApis(
-      12.month < 24.month,
-      "12.month < 24.month",
-      "INTERVAL '12' MONTH < INTERVAL '24' MONTH",
-      "true")
-
-    testAllApis(
-      8.milli > 10.milli,
-      "8.milli > 10.milli",
-      "INTERVAL '0.008' SECOND > INTERVAL '0.010' SECOND",
-      "false")
-
-    testAllApis(
-      8.year === 8.year,
-      "8.year === 8.year",
-      "INTERVAL '8' YEAR = INTERVAL '8' YEAR",
-      "true")
-
-    testAllApis(
-      8.year + 10.month,
-      "8.year + 10.month",
-      "INTERVAL '8' YEAR + INTERVAL '10' MONTH",
-      "+8-10")
-
-    testAllApis(
-      8.hour + 10.minute + 12.second + 5.milli,
-      "8.hour + 10.minute + 12.second + 5.milli",
-      "INTERVAL '8' HOUR + INTERVAL '10' MINUTE + INTERVAL '12.005' SECOND",
-      "+0 08:10:12.005")
-
-    testAllApis(
-      1.minute - 10.second,
-      "1.minute - 10.second",
-      "INTERVAL '1' MINUTE - INTERVAL '10' SECOND",
-      "+0 00:00:50.000")
-
-    testAllApis(
-      2.year - 12.month,
-      "2.year - 12.month",
-      "INTERVAL '2' YEAR - INTERVAL '12' MONTH",
-      "+1-00")
-
-    testAllApis(
-      -'f9.cast(Types.INTERVAL_MONTHS),
-      "-f9.cast(INTERVAL_MONTHS)",
-      "-CAST(f9 AS INTERVAL YEAR)",
-      "-2-00")
-
-    testAllApis(
-      'f0 + 2.day,
-      "f0 + 2.day",
-      "f0 + INTERVAL '2' DAY",
-      "1990-10-16")
-
-    testAllApis(
-      30.day + 'f0,
-      "30.day + f0",
-      "INTERVAL '30' DAY + f0",
-      "1990-11-13")
-
-    testAllApis(
-      'f1 + 12.hour,
-      "f1 + 12.hour",
-      "f1 + INTERVAL '12' HOUR",
-      "22:20:45")
-
-    testAllApis(
-      24.hour + 'f1,
-      "24.hour + f1",
-      "INTERVAL '24' HOUR + f1",
-      "10:20:45")
-
-    testAllApis(
-      'f2 + 10.day + 4.milli,
-      "f2 + 10.day + 4.milli",
-      "f2 + INTERVAL '10 00:00:00.004' DAY TO SECOND",
-      "1990-10-24 10:20:45.127")
-  }
-
-  // 
----------------------------------------------------------------------------------------------
-
-  def testData = {
-    val testData = new Row(11)
-    testData.setField(0, Date.valueOf("1990-10-14"))
-    testData.setField(1, Time.valueOf("10:20:45"))
-    testData.setField(2, Timestamp.valueOf("1990-10-14 10:20:45.123"))
-    testData.setField(3, Date.valueOf("1990-10-13"))
-    testData.setField(4, Date.valueOf("1990-10-15"))
-    testData.setField(5, Time.valueOf("00:00:00"))
-    testData.setField(6, Timestamp.valueOf("1990-10-14 00:00:00.0"))
-    testData.setField(7, 12000)
-    testData.setField(8, 1467012213000L)
-    testData.setField(9, 24)
-    testData.setField(10, 12000L)
-    testData
-  }
-
-  def typeInfo = {
-    new RowTypeInfo(Seq(
-      Types.DATE,
-      Types.TIME,
-      Types.TIMESTAMP,
-      Types.DATE,
-      Types.DATE,
-      Types.TIME,
-      Types.TIMESTAMP,
-      Types.INT,
-      Types.LONG,
-      Types.INTERVAL_MONTHS,
-      Types.INTERVAL_MILLIS)).asInstanceOf[TypeInformation[Any]]
-  }
-}

http://git-wip-us.apache.org/repos/asf/flink/blob/9a1bc021/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/SqlExpressionTest.scala
----------------------------------------------------------------------
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 0000000..cae4388
--- /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]]
+}

http://git-wip-us.apache.org/repos/asf/flink/blob/9a1bc021/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala
----------------------------------------------------------------------
diff --git 
a/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala
 
b/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala
new file mode 100644
index 0000000..24ff51c
--- /dev/null
+++ 
b/flink-libraries/flink-table/src/test/scala/org/apache/flink/api/table/expressions/TemporalTypesTest.scala
@@ -0,0 +1,389 @@
+/*
+ * 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 java.sql.{Date, Time, Timestamp}
+
+import org.apache.flink.api.common.typeinfo.TypeInformation
+import org.apache.flink.api.scala.table._
+import org.apache.flink.api.table.expressions.utils.ExpressionTestBase
+import org.apache.flink.api.table.typeutils.RowTypeInfo
+import org.apache.flink.api.table.{Row, Types}
+import org.junit.Test
+
+class TemporalTypesTest extends ExpressionTestBase {
+
+  @Test
+  def testTimePointLiterals(): Unit = {
+    testAllApis(
+      "1990-10-14".toDate,
+      "'1990-10-14'.toDate",
+      "DATE '1990-10-14'",
+      "1990-10-14")
+
+    testTableApi(
+      Date.valueOf("2040-09-11"),
+      "'2040-09-11'.toDate",
+      "2040-09-11")
+
+    testAllApis(
+      "1500-04-30".cast(Types.DATE),
+      "'1500-04-30'.cast(DATE)",
+      "CAST('1500-04-30' AS DATE)",
+      "1500-04-30")
+
+    testAllApis(
+      "15:45:59".toTime,
+      "'15:45:59'.toTime",
+      "TIME '15:45:59'",
+      "15:45:59")
+
+    testTableApi(
+      Time.valueOf("00:00:00"),
+      "'00:00:00'.toTime",
+      "00:00:00")
+
+    testAllApis(
+      "1:30:00".cast(Types.TIME),
+      "'1:30:00'.cast(TIME)",
+      "CAST('1:30:00' AS TIME)",
+      "01:30:00")
+
+    testAllApis(
+      "1990-10-14 23:00:00.123".toTimestamp,
+      "'1990-10-14 23:00:00.123'.toTimestamp",
+      "TIMESTAMP '1990-10-14 23:00:00.123'",
+      "1990-10-14 23:00:00.123")
+
+    testTableApi(
+      Timestamp.valueOf("2040-09-11 00:00:00.000"),
+      "'2040-09-11 00:00:00.000'.toTimestamp",
+      "2040-09-11 00:00:00.0")
+
+    testAllApis(
+      "1500-04-30 12:00:00".cast(Types.TIMESTAMP),
+      "'1500-04-30 12:00:00'.cast(TIMESTAMP)",
+      "CAST('1500-04-30 12:00:00' AS TIMESTAMP)",
+      "1500-04-30 12:00:00.0")
+  }
+
+  @Test
+  def testTimeIntervalLiterals(): Unit = {
+    testAllApis(
+      1.year,
+      "1.year",
+      "INTERVAL '1' YEAR",
+      "+1-00")
+
+    testAllApis(
+      1.month,
+      "1.month",
+      "INTERVAL '1' MONTH",
+      "+0-01")
+
+    testAllApis(
+      12.day,
+      "12.day",
+      "INTERVAL '12' DAY",
+      "+12 00:00:00.000")
+
+    testAllApis(
+      1.hour,
+      "1.hour",
+      "INTERVAL '1' HOUR",
+      "+0 01:00:00.000")
+
+    testAllApis(
+      3.minute,
+      "3.minute",
+      "INTERVAL '3' MINUTE",
+      "+0 00:03:00.000")
+
+    testAllApis(
+      3.second,
+      "3.second",
+      "INTERVAL '3' SECOND",
+      "+0 00:00:03.000")
+
+    testAllApis(
+      3.milli,
+      "3.milli",
+      "INTERVAL '0.003' SECOND",
+      "+0 00:00:00.003")
+  }
+
+  @Test
+  def testTimePointInput(): Unit = {
+    testAllApis(
+      'f0,
+      "f0",
+      "f0",
+      "1990-10-14")
+
+    testAllApis(
+      'f1,
+      "f1",
+      "f1",
+      "10:20:45")
+
+    testAllApis(
+      'f2,
+      "f2",
+      "f2",
+      "1990-10-14 10:20:45.123")
+  }
+
+  @Test
+  def testTimeIntervalInput(): Unit = {
+    testAllApis(
+      'f9,
+      "f9",
+      "f9",
+      "+2-00")
+
+    testAllApis(
+      'f10,
+      "f10",
+      "f10",
+      "+0 00:00:12.000")
+  }
+
+  @Test
+  def testTimePointCasting(): Unit = {
+    testAllApis(
+      'f0.cast(Types.TIMESTAMP),
+      "f0.cast(TIMESTAMP)",
+      "CAST(f0 AS TIMESTAMP)",
+      "1990-10-14 00:00:00.0")
+
+    testAllApis(
+      'f1.cast(Types.TIMESTAMP),
+      "f1.cast(TIMESTAMP)",
+      "CAST(f1 AS TIMESTAMP)",
+      "1970-01-01 10:20:45.0")
+
+    testAllApis(
+      'f2.cast(Types.DATE),
+      "f2.cast(DATE)",
+      "CAST(f2 AS DATE)",
+      "1990-10-14")
+
+    testAllApis(
+      'f2.cast(Types.TIME),
+      "f2.cast(TIME)",
+      "CAST(f2 AS TIME)",
+      "10:20:45")
+
+    testAllApis(
+      'f2.cast(Types.TIME),
+      "f2.cast(TIME)",
+      "CAST(f2 AS TIME)",
+      "10:20:45")
+
+    testTableApi(
+      'f7.cast(Types.DATE),
+      "f7.cast(DATE)",
+      "2002-11-09")
+
+    testTableApi(
+      'f7.cast(Types.DATE).cast(Types.INT),
+      "f7.cast(DATE).cast(INT)",
+      "12000")
+
+    testTableApi(
+      'f7.cast(Types.TIME),
+      "f7.cast(TIME)",
+      "00:00:12")
+
+    testTableApi(
+      'f7.cast(Types.TIME).cast(Types.INT),
+      "f7.cast(TIME).cast(INT)",
+      "12000")
+
+    testTableApi(
+      'f8.cast(Types.TIMESTAMP),
+      "f8.cast(TIMESTAMP)",
+      "2016-06-27 07:23:33.0")
+
+    testTableApi(
+      'f8.cast(Types.TIMESTAMP).cast(Types.LONG),
+      "f8.cast(TIMESTAMP).cast(LONG)",
+      "1467012213000")
+  }
+
+  @Test
+  def testTimeIntervalCasting(): Unit = {
+    testTableApi(
+      'f7.cast(Types.INTERVAL_MONTHS),
+      "f7.cast(INTERVAL_MONTHS)",
+      "+1000-00")
+
+    testTableApi(
+      'f8.cast(Types.INTERVAL_MILLIS),
+      "f8.cast(INTERVAL_MILLIS)",
+      "+16979 07:23:33.000")
+  }
+
+  @Test
+  def testTimePointComparison(): Unit = {
+    testAllApis(
+      'f0 < 'f3,
+      "f0 < f3",
+      "f0 < f3",
+      "false")
+
+    testAllApis(
+      'f0 < 'f4,
+      "f0 < f4",
+      "f0 < f4",
+      "true")
+
+    testAllApis(
+      'f1 < 'f5,
+      "f1 < f5",
+      "f1 < f5",
+      "false")
+
+    testAllApis(
+      'f0.cast(Types.TIMESTAMP) !== 'f2,
+      "f0.cast(TIMESTAMP) !== f2",
+      "CAST(f0 AS TIMESTAMP) <> f2",
+      "true")
+
+    testAllApis(
+      'f0.cast(Types.TIMESTAMP) === 'f6,
+      "f0.cast(TIMESTAMP) === f6",
+      "CAST(f0 AS TIMESTAMP) = f6",
+      "true")
+  }
+
+  @Test
+  def testTimeIntervalArithmetic(): Unit = {
+    testAllApis(
+      12.month < 24.month,
+      "12.month < 24.month",
+      "INTERVAL '12' MONTH < INTERVAL '24' MONTH",
+      "true")
+
+    testAllApis(
+      8.milli > 10.milli,
+      "8.milli > 10.milli",
+      "INTERVAL '0.008' SECOND > INTERVAL '0.010' SECOND",
+      "false")
+
+    testAllApis(
+      8.year === 8.year,
+      "8.year === 8.year",
+      "INTERVAL '8' YEAR = INTERVAL '8' YEAR",
+      "true")
+
+    testAllApis(
+      8.year + 10.month,
+      "8.year + 10.month",
+      "INTERVAL '8' YEAR + INTERVAL '10' MONTH",
+      "+8-10")
+
+    testAllApis(
+      8.hour + 10.minute + 12.second + 5.milli,
+      "8.hour + 10.minute + 12.second + 5.milli",
+      "INTERVAL '8' HOUR + INTERVAL '10' MINUTE + INTERVAL '12.005' SECOND",
+      "+0 08:10:12.005")
+
+    testAllApis(
+      1.minute - 10.second,
+      "1.minute - 10.second",
+      "INTERVAL '1' MINUTE - INTERVAL '10' SECOND",
+      "+0 00:00:50.000")
+
+    testAllApis(
+      2.year - 12.month,
+      "2.year - 12.month",
+      "INTERVAL '2' YEAR - INTERVAL '12' MONTH",
+      "+1-00")
+
+    testAllApis(
+      -'f9.cast(Types.INTERVAL_MONTHS),
+      "-f9.cast(INTERVAL_MONTHS)",
+      "-CAST(f9 AS INTERVAL YEAR)",
+      "-2-00")
+
+    testAllApis(
+      'f0 + 2.day,
+      "f0 + 2.day",
+      "f0 + INTERVAL '2' DAY",
+      "1990-10-16")
+
+    testAllApis(
+      30.day + 'f0,
+      "30.day + f0",
+      "INTERVAL '30' DAY + f0",
+      "1990-11-13")
+
+    testAllApis(
+      'f1 + 12.hour,
+      "f1 + 12.hour",
+      "f1 + INTERVAL '12' HOUR",
+      "22:20:45")
+
+    testAllApis(
+      24.hour + 'f1,
+      "24.hour + f1",
+      "INTERVAL '24' HOUR + f1",
+      "10:20:45")
+
+    testAllApis(
+      'f2 + 10.day + 4.milli,
+      "f2 + 10.day + 4.milli",
+      "f2 + INTERVAL '10 00:00:00.004' DAY TO SECOND",
+      "1990-10-24 10:20:45.127")
+  }
+
+  // 
----------------------------------------------------------------------------------------------
+
+  def testData = {
+    val testData = new Row(11)
+    testData.setField(0, Date.valueOf("1990-10-14"))
+    testData.setField(1, Time.valueOf("10:20:45"))
+    testData.setField(2, Timestamp.valueOf("1990-10-14 10:20:45.123"))
+    testData.setField(3, Date.valueOf("1990-10-13"))
+    testData.setField(4, Date.valueOf("1990-10-15"))
+    testData.setField(5, Time.valueOf("00:00:00"))
+    testData.setField(6, Timestamp.valueOf("1990-10-14 00:00:00.0"))
+    testData.setField(7, 12000)
+    testData.setField(8, 1467012213000L)
+    testData.setField(9, 24)
+    testData.setField(10, 12000L)
+    testData
+  }
+
+  def typeInfo = {
+    new RowTypeInfo(Seq(
+      Types.DATE,
+      Types.TIME,
+      Types.TIMESTAMP,
+      Types.DATE,
+      Types.DATE,
+      Types.TIME,
+      Types.TIMESTAMP,
+      Types.INT,
+      Types.LONG,
+      Types.INTERVAL_MONTHS,
+      Types.INTERVAL_MILLIS)).asInstanceOf[TypeInformation[Any]]
+  }
+}

Reply via email to