You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2017/12/11 23:15:11 UTC

[1/4] calcite git commit: [CALCITE-2080] Query with NOT IN operator and literal throws AssertionError: 'Cast for just nullability not allowed' (Volodymyr Vysotskyi)

Repository: calcite
Updated Branches:
  refs/heads/master 314b5b78b -> b60b67eb8


[CALCITE-2080] Query with NOT IN operator and literal throws AssertionError: 'Cast for just nullability not allowed' (Volodymyr Vysotskyi)

Alternative fix (in SqlToRelConverter rather than RelOptUtil);
fix query formatting in SqlToRelConverterTest. (Julian Hyde)

Close apache/calcite#579


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/04cbdb2c
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/04cbdb2c
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/04cbdb2c

Branch: refs/heads/master
Commit: 04cbdb2cead5985322db737e1b0b8ce2259a7306
Parents: 314b5b7
Author: Volodymyr Vysotskyi <vv...@gmail.com>
Authored: Thu Dec 7 18:26:38 2017 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Dec 11 02:11:11 2017 -0800

----------------------------------------------------------------------
 .../rel/rules/FilterProjectTransposeRule.java   |  10 +-
 .../org/apache/calcite/rex/RexSimplify.java     |   5 +-
 .../java/org/apache/calcite/rex/RexUtil.java    |  11 ++
 .../calcite/sql2rel/SqlToRelConverter.java      |   6 +-
 .../calcite/test/SqlToRelConverterTest.java     |  96 +++++++++---------
 .../calcite/test/SqlToRelConverterTest.xml      | 101 ++++++++++++-------
 6 files changed, 134 insertions(+), 95 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/04cbdb2c/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
index aaec8a8..3bdb5f5 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.rel.rules;
 
-import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptRuleOperand;
@@ -27,7 +26,6 @@ import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexOver;
-import org.apache.calcite.rex.RexSimplify;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -120,13 +118,9 @@ public class FilterProjectTransposeRule extends RelOptRule {
     final RelBuilder relBuilder = call.builder();
     RelNode newFilterRel;
     if (copyFilter) {
-      final RelOptPredicateList predicates = RelOptPredicateList.EMPTY;
-      final RexSimplify simplify =
-          new RexSimplify(relBuilder.getRexBuilder(), predicates, false,
-              RexUtil.EXECUTOR);
-      newCondition = simplify.removeNullabilityCast(newCondition);
       newFilterRel = filter.copy(filter.getTraitSet(), project.getInput(),
-          newCondition);
+          RexUtil.removeNullabilityCast(relBuilder.getTypeFactory(),
+              newCondition));
     } else {
       newFilterRel =
           relBuilder.push(project.getInput()).filter(newCondition).build();

http://git-wip-us.apache.org/repos/asf/calcite/blob/04cbdb2c/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index cd42c81..34e3814 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -953,10 +953,7 @@ public class RexSimplify {
    *
    * <p>For example, {@code CAST(1 = 0 AS BOOLEAN)} becomes {@code 1 = 0}. */
   public RexNode removeNullabilityCast(RexNode e) {
-    while (RexUtil.isNullabilityCast(rexBuilder.getTypeFactory(), e)) {
-      e = ((RexCall) e).operands.get(0);
-    }
-    return e;
+    return RexUtil.removeNullabilityCast(rexBuilder.getTypeFactory(), e);
   }
 
   private static <C extends Comparable<C>> RexNode processRange(

http://git-wip-us.apache.org/repos/asf/calcite/blob/04cbdb2c/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 1dd0396..6f467be 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -310,6 +310,17 @@ public class RexUtil {
     return false;
   }
 
+  /** Removes any casts that change nullability but not type.
+   *
+   * <p>For example, {@code CAST(1 = 0 AS BOOLEAN)} becomes {@code 1 = 0}. */
+  public static RexNode removeNullabilityCast(RelDataTypeFactory typeFactory,
+      RexNode node) {
+    while (isNullabilityCast(typeFactory, node)) {
+      node = ((RexCall) node).operands.get(0);
+    }
+    return node;
+  }
+
   /** Creates a map containing each (e, constant) pair that occurs within
    * a predicate list.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/04cbdb2c/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index ede2daf..e1b0d17 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -963,15 +963,17 @@ public class SqlToRelConverter {
     SqlNode newWhere = pushDownNotForIn(bb.scope, where);
     replaceSubQueries(bb, newWhere, RelOptUtil.Logic.UNKNOWN_AS_FALSE);
     final RexNode convertedWhere = bb.convertExpression(newWhere);
+    final RexNode convertedWhere2 =
+        RexUtil.removeNullabilityCast(typeFactory, convertedWhere);
 
     // only allocate filter if the condition is not TRUE
-    if (convertedWhere.isAlwaysTrue()) {
+    if (convertedWhere2.isAlwaysTrue()) {
       return;
     }
 
     final RelFactories.FilterFactory factory =
         RelFactories.DEFAULT_FILTER_FACTORY;
-    final RelNode filter = factory.createFilter(bb.root, convertedWhere);
+    final RelNode filter = factory.createFilter(bb.root, convertedWhere2);
     final RelNode r;
     final CorrelationUse p = getCorrelationUse(bb, filter);
     if (p != null) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/04cbdb2c/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index de0b268..6c3651a 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -2371,6 +2371,19 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
     sql(sql).with(getTesterWithDynamicTable()).ok();
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-2080">[CALCITE-2080]
+   * Query with NOT IN operator and literal fails throws AssertionError: 'Cast
+   * for just nullability not allowed'</a>. */
+  @Test public void testNotInWithLiteral() {
+    final String sql = "SELECT *\n"
+        + "FROM SALES.NATION\n"
+        + "WHERE n_name NOT IN\n"
+        + "    (SELECT ''\n"
+        + "     FROM SALES.NATION)";
+    sql(sql).with(getTesterWithDynamicTable()).ok();
+  }
+
   /**
    * Test case for Dynamic Table / Dynamic Star support
    * <a href="https://issues.apache.org/jira/browse/CALCITE-1150">[CALCITE-1150]</a>
@@ -2570,27 +2583,24 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
         + "    pattern (strt down+ up+)\n"
         + "    define\n"
         + "      down as down.mgr < PREV(down.mgr),\n"
-        + "      up as up.mgr > prev(up.mgr)\n"
-        + "  ) mr";
+        + "      up as up.mgr > prev(up.mgr)) as mr";
     sql(sql).ok();
   }
 
   @Test public void testMatchRecognizeMeasures1() {
     final String sql = "select *\n"
-        + "  from emp match_recognize\n"
-        + "  (\n"
-        + "   partition by job, sal\n"
-        + "   order by job asc, sal desc\n"
-        + "   measures  MATCH_NUMBER() as match_num, "
-        + "   CLASSIFIER() as var_match, "
-        + "   STRT.mgr as start_nw,"
-        + "   LAST(DOWN.mgr) as bottom_nw,"
-        + "   LAST(up.mgr) as end_nw"
-        + "    pattern (strt down+ up+)\n"
-        + "    define\n"
-        + "      down as down.mgr < PREV(down.mgr),\n"
-        + "      up as up.mgr > prev(up.mgr)\n"
-        + "  ) mr";
+        + "from emp match_recognize (\n"
+        + "  partition by job, sal\n"
+        + "  order by job asc, sal desc\n"
+        + "  measures MATCH_NUMBER() as match_num,\n"
+        + "    CLASSIFIER() as var_match,\n"
+        + "    STRT.mgr as start_nw,\n"
+        + "    LAST(DOWN.mgr) as bottom_nw,\n"
+        + "    LAST(up.mgr) as end_nw\n"
+        + "  pattern (strt down+ up+)\n"
+        + "  define\n"
+        + "    down as down.mgr < PREV(down.mgr),\n"
+        + "    up as up.mgr > prev(up.mgr)) as mr";
     sql(sql).ok();
   }
 
@@ -2600,40 +2610,36 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
    * columns</a>. */
   @Test public void testMatchRecognizeMeasures2() {
     final String sql = "select *\n"
-        + "  from emp match_recognize\n"
-        + "  (\n"
-        + "   partition by job\n"
-        + "   order by sal\n"
-        + "   measures  MATCH_NUMBER() as match_num, "
-        + "   CLASSIFIER() as var_match, "
-        + "   STRT.mgr as start_nw,"
-        + "   LAST(DOWN.mgr) as bottom_nw,"
-        + "   LAST(up.mgr) as end_nw"
-        + "    pattern (strt down+ up+)\n"
-        + "    define\n"
-        + "      down as down.mgr < PREV(down.mgr),\n"
-        + "      up as up.mgr > prev(up.mgr)\n"
-        + "  ) mr";
+        + "from emp match_recognize (\n"
+        + "  partition by job\n"
+        + "  order by sal\n"
+        + "  measures MATCH_NUMBER() as match_num,\n"
+        + "    CLASSIFIER() as var_match,\n"
+        + "    STRT.mgr as start_nw,\n"
+        + "    LAST(DOWN.mgr) as bottom_nw,\n"
+        + "    LAST(up.mgr) as end_nw\n"
+        + "  pattern (strt down+ up+)\n"
+        + "  define\n"
+        + "    down as down.mgr < PREV(down.mgr),\n"
+        + "    up as up.mgr > prev(up.mgr)) as mr";
     sql(sql).ok();
   }
 
   @Test public void testMatchRecognizeMeasures3() {
     final String sql = "select *\n"
-        + "  from emp match_recognize\n"
-        + "  (\n"
-        + "   partition by job\n"
-        + "   order by sal\n"
-        + "   measures  MATCH_NUMBER() as match_num, "
-        + "   CLASSIFIER() as var_match, "
-        + "   STRT.mgr as start_nw,"
-        + "   LAST(DOWN.mgr) as bottom_nw,"
-        + "   LAST(up.mgr) as end_nw"
-        + "   ALL ROWS PER MATCH"
-        + "    pattern (strt down+ up+)\n"
-        + "    define\n"
-        + "      down as down.mgr < PREV(down.mgr),\n"
-        + "      up as up.mgr > prev(up.mgr)\n"
-        + "  ) mr";
+        + "from emp match_recognize (\n"
+        + "  partition by job\n"
+        + "  order by sal\n"
+        + "  measures MATCH_NUMBER() as match_num,\n"
+        + "    CLASSIFIER() as var_match,\n"
+        + "    STRT.mgr as start_nw,\n"
+        + "    LAST(DOWN.mgr) as bottom_nw,\n"
+        + "    LAST(up.mgr) as end_nw\n"
+        + "  ALL ROWS PER MATCH\n"
+        + "  pattern (strt down+ up+)\n"
+        + "  define\n"
+        + "    down as down.mgr < PREV(down.mgr),\n"
+        + "    up as up.mgr > prev(up.mgr)) as mr";
     sql(sql).ok();
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/04cbdb2c/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index c7d2b24..de4a0c1 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -361,7 +361,7 @@ window w as (partition by productId)]]>
     </TestCase>
     <TestCase name="testArrayOfRecord">
         <Resource name="sql">
-            <![CDATA[select*from unnest(array(select*from dept))]]>
+            <![CDATA[select employees[1].skills[2+3].desc from dept_nested]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -648,7 +648,7 @@ LogicalProject(EXPR$0=[CASE(IS NULL($0), IS NOT NULL($1), IS NULL($1), IS NOT NU
         <Resource name="sql">
             <![CDATA[select empno is distinct from deptno
 from (values (cast(null as int), 1),
-              (2, cast(null as int))) as emp(empno, deptno)]]>
+             (2, cast(null as int))) as emp(empno, deptno)]]>
         </Resource>
     </TestCase>
     <TestCase name="testIsNotDistinctFrom">
@@ -1511,7 +1511,7 @@ LogicalSort(offset=[10], fetch=[5])
 ]]>
         </Resource>
     </TestCase>
-<TestCase name="testOffsetFetchWithDynamicParameter">
+    <TestCase name="testOffsetFetchWithDynamicParameter">
         <Resource name="sql">
             <![CDATA[select empno from emp
 offset ? rows fetch next ? rows only]]>
@@ -2444,8 +2444,7 @@ LogicalProject(DEPTNO=[$1], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
     pattern (strt down+ up+)
     define
       down as down.mgr < PREV(down.mgr),
-      up as up.mgr > prev(up.mgr)
-  ) mr]]>
+      up as up.mgr > prev(up.mgr)) as mr]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2458,15 +2457,18 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
     <TestCase name="testMatchRecognizeMeasures1">
         <Resource name="sql">
             <![CDATA[select *
-  from emp match_recognize
-  (
-   partition by job, sal
-   order by job asc, sal desc
-   measures STRT.mgr as start_nw,   LAST(DOWN.mgr) as bottom_nw,   LAST(up.mgr) as end_nw    pattern (strt down+ up+)
-    define
-      down as down.mgr < PREV(down.mgr),
-      up as up.mgr > prev(up.mgr)
-  ) mr]]>
+from emp match_recognize (
+  partition by job, sal
+  order by job asc, sal desc
+  measures MATCH_NUMBER() as match_num,
+    CLASSIFIER() as var_match,
+    STRT.mgr as start_nw,
+    LAST(DOWN.mgr) as bottom_nw,
+    LAST(up.mgr) as end_nw
+  pattern (strt down+ up+)
+  define
+    down as down.mgr < PREV(down.mgr),
+    up as up.mgr > prev(up.mgr)) as mr]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2479,15 +2481,18 @@ LogicalProject(JOB=[$0], SAL=[$1], MATCH_NUM=[$2], VAR_MATCH=[$3], START_NW=[$4]
     <TestCase name="testMatchRecognizeMeasures2">
         <Resource name="sql">
             <![CDATA[select *
-  from emp match_recognize
-  (
-   partition by job
-   order by sal
-   measures STRT.mgr as start_nw,   LAST(DOWN.mgr) as bottom_nw,   LAST(up.mgr) as end_nw    pattern (strt down+ up+)
-    define
-      down as down.mgr < PREV(down.mgr),
-      up as up.mgr > prev(up.mgr)
-  ) mr]]>
+from emp match_recognize (
+  partition by job
+  order by sal
+  measures MATCH_NUMBER() as match_num,
+    CLASSIFIER() as var_match,
+    STRT.mgr as start_nw,
+    LAST(DOWN.mgr) as bottom_nw,
+    LAST(up.mgr) as end_nw
+  pattern (strt down+ up+)
+  define
+    down as down.mgr < PREV(down.mgr),
+    up as up.mgr > prev(up.mgr)) as mr]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2500,17 +2505,19 @@ LogicalProject(JOB=[$0], MATCH_NUM=[$1], VAR_MATCH=[$2], START_NW=[$3], BOTTOM_N
     <TestCase name="testMatchRecognizeMeasures3">
         <Resource name="sql">
             <![CDATA[select *
-  from emp match_recognize
-  (
-   partition by job
-   order by sal
-   measures STRT.mgr as start_nw,   LAST(DOWN.mgr) as bottom_nw,   LAST(up.mgr) as end_nw
-   ALL ROWS PER MATCH
-   pattern (strt down+ up+)
-    define
-      down as down.mgr < PREV(down.mgr),
-      up as up.mgr > prev(up.mgr)
-  ) mr]]>
+from emp match_recognize (
+  partition by job
+  order by sal
+  measures MATCH_NUMBER() as match_num,
+    CLASSIFIER() as var_match,
+    STRT.mgr as start_nw,
+    LAST(DOWN.mgr) as bottom_nw,
+    LAST(up.mgr) as end_nw
+  ALL ROWS PER MATCH
+  pattern (strt down+ up+)
+  define
+    down as down.mgr < PREV(down.mgr),
+    up as up.mgr > prev(up.mgr)) as mr]]>
         </Resource>
         <Resource name="plan">
             <![CDATA[
@@ -2543,7 +2550,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
     <TestCase name="testMatchRecognizePrevDown">
         <Resource name="sql">
             <![CDATA[SELECT *
-FROM tmp
+FROM emp
 MATCH_RECOGNIZE (
   MEASURES
     STRT.mgr AS start_mgr,
@@ -2611,7 +2618,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$
     <TestCase name="testPrevClassifier">
         <Resource name="sql">
             <![CDATA[SELECT *
-FROM tmp
+FROM emp
 MATCH_RECOGNIZE (
   MEASURES
     STRT.mgr AS start_mgr,
@@ -2916,6 +2923,28 @@ LogicalDelta
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testNotInWithLiteral">
+        <Resource name="sql">
+            <![CDATA[SELECT *
+FROM SALES.NATION
+WHERE n_name NOT IN
+    (SELECT ''
+     FROM SALES.NATION)]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalProject(**=[$1])
+  LogicalFilter(condition=[NOT(AND(IS TRUE($4), IS NOT NULL($2)))])
+    LogicalJoin(condition=[=($2, $3)], joinType=[left])
+      LogicalProject($f0=[$0], $f1=[$1], $f2=[$0])
+        LogicalTableScan(table=[[CATALOG, SALES, NATION]])
+      LogicalAggregate(group=[{0}], agg#0=[MIN($1)])
+        LogicalProject($f0=[$0], $f1=[true])
+          LogicalProject(EXPR$0=[''])
+            LogicalTableScan(table=[[CATALOG, SALES, NATION]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testSessionTable">
         <Resource name="sql">
             <![CDATA[select stream session_start(rowtime, interval '1' hour) as rowtime,


[3/4] calcite git commit: [CALCITE-2066] RelOptUtil.splitJoinCondition() could not split CASE condition after applying FilterReduceExpressionsRule (Volodymyr Vysotskyi)

Posted by jh...@apache.org.
[CALCITE-2066] RelOptUtil.splitJoinCondition() could not split CASE condition after applying FilterReduceExpressionsRule (Volodymyr Vysotskyi)

The solution is to less aggressively push "=" into CASE.

Close apache/calcite#573


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

Branch: refs/heads/master
Commit: b60b67eb8f62463ccbc230358969ef2450cdbe05
Parents: c1749ad
Author: Volodymyr Vysotskyi <vv...@gmail.com>
Authored: Tue Nov 28 14:22:12 2017 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Dec 11 14:24:53 2017 -0800

----------------------------------------------------------------------
 .../rel/rules/ReduceExpressionsRule.java        | 12 ++++++++++
 .../apache/calcite/test/RelOptRulesTest.java    | 13 +++++++++++
 .../org/apache/calcite/test/RelOptRulesTest.xml | 23 ++++++++++++++++++++
 3 files changed, 48 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/b60b67eb/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index 47a859c..264d123 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -20,6 +20,7 @@ import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.EquiJoin;
@@ -61,6 +62,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
+import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
@@ -702,6 +704,16 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
     case AND:
     case OR:
       return call; // don't push CASE into CASE!
+    case EQUALS: {
+      // checks that the EQUALS operands may be splitted and
+      // doesn't push EQUALS into CASE
+      List<RexNode> equalsOperands = call.getOperands();
+      ImmutableBitSet left = RelOptUtil.InputFinder.bits(equalsOperands.get(0));
+      ImmutableBitSet right = RelOptUtil.InputFinder.bits(equalsOperands.get(1));
+      if (!left.isEmpty() && !right.isEmpty() && left.intersect(right).isEmpty()) {
+        return call;
+      }
+    }
     }
     int caseOrdinal = -1;
     final List<RexNode> operands = call.getOperands();

http://git-wip-us.apache.org/repos/asf/calcite/blob/b60b67eb/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 7e1d1ec..b711ec8 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -1609,6 +1609,19 @@ public class RelOptRulesTest extends RelOptTestBase {
             + "  else 0 end = 1");
   }
 
+  @Test
+  public void testSkipReduceConstantsCaseEquals() throws Exception {
+    HepProgram program = new HepProgramBuilder()
+        .addRuleInstance(ReduceExpressionsRule.PROJECT_INSTANCE)
+        .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)
+        .addRuleInstance(FilterJoinRule.FilterIntoJoinRule.FILTER_ON_JOIN)
+        .build();
+
+    checkPlanning(program,
+        "select * from emp e1, emp e2\n"
+            + "where coalesce(e1.mgr, -1) = coalesce(e2.mgr, -1)");
+  }
+
   @Test public void testReduceConstantsEliminatesFilter() throws Exception {
     HepProgram program = new HepProgramBuilder()
         .addRuleInstance(ReduceExpressionsRule.FILTER_INSTANCE)

http://git-wip-us.apache.org/repos/asf/calcite/blob/b60b67eb/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 8061eac..1b2287b 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -6128,6 +6128,29 @@ LogicalAggregate(group=[{}], EXPR$0=[COUNT()])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testSkipReduceConstantsCaseEquals">
+        <Resource name="sql">
+            <![CDATA[select * from emp e1, emp e2
+where coalesce(e1.mgr, -1) = coalesce(e2.mgr, -1)]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EMPNO0=[$9], ENAME0=[$10], JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], COMM0=[$15], DEPTNO0=[$16], SLACKER0=[$17])
+  LogicalFilter(condition=[=(CASE(IS NOT NULL($3), $3, -1), CASE(IS NOT NULL($12), $12, -1))])
+    LogicalJoin(condition=[true], joinType=[inner])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EMPNO0=[$9], ENAME0=[$10], JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], COMM0=[$15], DEPTNO0=[$16], SLACKER0=[$17])
+  LogicalJoin(condition=[=(CASE(IS NOT NULL($3), $3, -1), CASE(IS NOT NULL($12), $12, -1))], joinType=[inner])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testPushFilterPastAggThree">
         <Resource name="sql">
             <![CDATA[select deptno from emp


[2/4] calcite git commit: [CALCITE-2084] SqlValidatorImpl.findTable() method incorrectly handles table schema with few schema levels (Volodymyr Vysotskyi)

Posted by jh...@apache.org.
[CALCITE-2084] SqlValidatorImpl.findTable() method incorrectly handles table schema with few schema levels (Volodymyr Vysotskyi)

Close apache/calcite#580


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/26b4712d
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/26b4712d
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/26b4712d

Branch: refs/heads/master
Commit: 26b4712d4ac36c49aa8c80b2f0841be37c9de1df
Parents: 04cbdb2
Author: Volodymyr Vysotskyi <vv...@gmail.com>
Authored: Sun Dec 10 14:08:47 2017 +0200
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Dec 11 14:24:53 2017 -0800

----------------------------------------------------------------------
 .../calcite/prepare/CalciteCatalogReader.java   | 65 ++++------------
 .../calcite/sql/validate/SqlValidatorImpl.java  | 13 +---
 .../calcite/sql/validate/SqlValidatorUtil.java  | 79 ++++++++++++++++++++
 .../apache/calcite/sql/test/SqlAdvisorTest.java |  4 +-
 .../apache/calcite/test/MockCatalogReader.java  | 33 +++++++-
 .../apache/calcite/test/SqlValidatorTest.java   |  3 +
 6 files changed, 130 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/26b4712d/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
index b7f32e1..af0d3a6 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -114,36 +114,7 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
   public Prepare.PreparingTable getTable(final List<String> names) {
     // First look in the default schema, if any.
     // If not found, look in the root schema.
-    for (List<String> schemaPath : schemaPaths) {
-      Prepare.PreparingTable table =
-          getTableFrom(names, schemaPath, nameMatcher);
-      if (table != null) {
-        return table;
-      }
-    }
-    return null;
-  }
-
-  @Override public CalciteConnectionConfig getConfig() {
-    return config;
-  }
-
-  private Prepare.PreparingTable getTableFrom(List<String> names,
-                                              List<String> schemaNames,
-                                              SqlNameMatcher nameMatcher) {
-    CalciteSchema schema =
-        getSchema(Iterables.concat(schemaNames, Util.skipLast(names)),
-            nameMatcher);
-    if (schema == null) {
-      return null;
-    }
-    final String name = Util.last(names);
-    CalciteSchema.TableEntry entry =
-        schema.getTable(name, nameMatcher.isCaseSensitive());
-    if (entry == null) {
-      entry = schema.getTableBasedOnNullaryFunction(name,
-          nameMatcher.isCaseSensitive());
-    }
+    CalciteSchema.TableEntry entry = SqlValidatorUtil.getTableEntry(this, names);
     if (entry != null) {
       final Table table = entry.getTable();
       if (table instanceof Wrapper) {
@@ -153,12 +124,16 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
           return relOptTable;
         }
       }
-      return RelOptTableImpl.create(this, table.getRowType(typeFactory), entry,
-          null);
+      return RelOptTableImpl.create(this,
+          table.getRowType(typeFactory), entry, null);
     }
     return null;
   }
 
+  @Override public CalciteConnectionConfig getConfig() {
+    return config;
+  }
+
   private Collection<Function> getFunctionsFrom(List<String> names) {
     final List<Function> functions2 = Lists.newArrayList();
     final List<List<String>> schemaNameList = new ArrayList<>();
@@ -172,7 +147,8 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
       }
     } else {
       for (List<String> schemaPath : schemaPaths) {
-        CalciteSchema schema = getSchema(schemaPath, nameMatcher);
+        CalciteSchema schema =
+            SqlValidatorUtil.getSchema(rootSchema, schemaPath, nameMatcher);
         if (schema != null) {
           schemaNameList.addAll(schema.getPath());
         }
@@ -180,8 +156,8 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
     }
     for (List<String> schemaNames : schemaNameList) {
       CalciteSchema schema =
-          getSchema(Iterables.concat(schemaNames, Util.skipLast(names)),
-              nameMatcher);
+          SqlValidatorUtil.getSchema(rootSchema,
+              Iterables.concat(schemaNames, Util.skipLast(names)), nameMatcher);
       if (schema != null) {
         final String name = Util.last(names);
         functions2.addAll(schema.getFunctions(name, true));
@@ -190,28 +166,13 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
     return functions2;
   }
 
-  private CalciteSchema getSchema(Iterable<String> schemaNames,
-      SqlNameMatcher nameMatcher) {
-    CalciteSchema schema = rootSchema;
-    for (String schemaName : schemaNames) {
-      if (schema == rootSchema
-          && nameMatcher.matches(schemaName, schema.getName())) {
-        continue;
-      }
-      schema = schema.getSubSchema(schemaName, nameMatcher.isCaseSensitive());
-      if (schema == null) {
-        return null;
-      }
-    }
-    return schema;
-  }
-
   public RelDataType getNamedType(SqlIdentifier typeName) {
     return null;
   }
 
   public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) {
-    final CalciteSchema schema = getSchema(names, nameMatcher);
+    final CalciteSchema schema =
+        SqlValidatorUtil.getSchema(rootSchema, names, nameMatcher);
     if (schema == null) {
       return ImmutableList.of();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/26b4712d/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 7c7c065..652bbe9 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -3440,17 +3440,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
               catalogReader.nameMatcher().isCaseSensitive());
     }
 
-    String schemaName = names.get(0);
-    String tableName = names.get(1);
-
-    CalciteSchema schema = catalogReader.getRootSchema().getSubSchemaMap().get(schemaName);
-
-    if (schema == null) {
-      return null;
-    }
-
-    CalciteSchema.TableEntry entry = schema.getTable(tableName,
-            catalogReader.nameMatcher().isCaseSensitive());
+    CalciteSchema.TableEntry entry =
+        SqlValidatorUtil.getTableEntry(catalogReader, names);
 
     return entry == null ? null : entry.getTable();
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/26b4712d/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
index 2200e01..8583cb1 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql.validate;
 
+import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.linq4j.Linq4j;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.plan.RelOptSchemaWithSampling;
@@ -51,6 +52,7 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
@@ -931,6 +933,83 @@ public class SqlValidatorUtil {
   }
 
   /**
+   * Finds a {@link org.apache.calcite.jdbc.CalciteSchema.TableEntry} in a
+   * given catalog reader whose table has the given name, possibly qualified.
+   *
+   * <p>Uses the case-sensitivity policy of the specified catalog reader.
+   *
+   * <p>If not found, returns null.
+   *
+   * @param catalogReader accessor to the table metadata
+   * @param names Name of table, may be qualified or fully-qualified
+   *
+   * @return TableEntry with a table with the given name, or null
+   */
+  public static CalciteSchema.TableEntry getTableEntry(
+      SqlValidatorCatalogReader catalogReader, List<String> names) {
+    // First look in the default schema, if any.
+    // If not found, look in the root schema.
+    for (List<String> schemaPath : catalogReader.getSchemaPaths()) {
+      CalciteSchema schema =
+          getSchema(catalogReader.getRootSchema(),
+              Iterables.concat(schemaPath, Util.skipLast(names)),
+              catalogReader.nameMatcher());
+      if (schema == null) {
+        continue;
+      }
+      CalciteSchema.TableEntry entry =
+          getTableEntryFrom(schema, Util.last(names),
+              catalogReader.nameMatcher().isCaseSensitive());
+      if (entry != null) {
+        return entry;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Finds and returns {@link CalciteSchema} nested to the given rootSchema
+   * with specified schemaPath.
+   *
+   * <p>Uses the case-sensitivity policy of specified nameMatcher.
+   *
+   * <p>If not found, returns null.
+   *
+   * @param rootSchema root schema
+   * @param schemaPath full schema path of required schema
+   * @param nameMatcher name matcher
+   *
+   * @return CalciteSchema that corresponds specified schemaPath
+   */
+  public static CalciteSchema getSchema(CalciteSchema rootSchema,
+      Iterable<String> schemaPath, SqlNameMatcher nameMatcher) {
+    CalciteSchema schema = rootSchema;
+    for (String schemaName : schemaPath) {
+      if (schema == rootSchema
+          && nameMatcher.matches(schemaName, schema.getName())) {
+        continue;
+      }
+      schema = schema.getSubSchema(schemaName,
+          nameMatcher.isCaseSensitive());
+      if (schema == null) {
+        return null;
+      }
+    }
+    return schema;
+  }
+
+  private static CalciteSchema.TableEntry getTableEntryFrom(
+      CalciteSchema schema, String name, boolean caseSensitive) {
+    CalciteSchema.TableEntry entry =
+        schema.getTable(name, caseSensitive);
+    if (entry == null) {
+      entry = schema.getTableBasedOnNullaryFunction(name,
+          caseSensitive);
+    }
+    return entry;
+  }
+
+  /**
    * Returns whether there are any input columns that are sorted.
    *
    * <p>If so, it can be the default ORDER BY clause for a WINDOW specification.

http://git-wip-us.apache.org/repos/asf/calcite/blob/26b4712d/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index e0f0d94..e877b2a 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -68,6 +68,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
   protected static final List<String> SALES_TABLES =
       Arrays.asList(
           "SCHEMA(CATALOG.SALES)",
+          "SCHEMA(CATALOG.SALES.NEST)",
           "TABLE(CATALOG.SALES.EMP)",
           "TABLE(CATALOG.SALES.EMPDEFAULTS)",
           "TABLE(CATALOG.SALES.EMPNULLABLES)",
@@ -92,7 +93,8 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
           "SCHEMA(CATALOG.DYNAMIC)",
           "SCHEMA(CATALOG.SALES)",
           "SCHEMA(CATALOG.STRUCT)",
-          "SCHEMA(CATALOG.CUSTOMER)");
+          "SCHEMA(CATALOG.CUSTOMER)",
+          "SCHEMA(CATALOG.SALES.NEST)");
 
   private static final List<String> AB_TABLES =
       Arrays.asList(

http://git-wip-us.apache.org/repos/asf/calcite/blob/26b4712d/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
index 5f96848..cd42747 100644
--- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
+++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
@@ -87,6 +87,7 @@ import org.apache.calcite.sql.validate.SqlMonotonicity;
 import org.apache.calcite.sql.validate.SqlNameMatcher;
 import org.apache.calcite.sql.validate.SqlNameMatchers;
 import org.apache.calcite.sql.validate.SqlValidatorCatalogReader;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.sql2rel.InitializerContext;
 import org.apache.calcite.sql2rel.InitializerExpressionFactory;
 import org.apache.calcite.sql2rel.NullInitializerExpressionFactory;
@@ -527,6 +528,20 @@ public class MockCatalogReader extends CalciteCatalogReader {
     deptSlackingTable.addColumn("SLACKINGMIN", f.intType);
     registerTable(deptSlackingTable);
 
+    // Register nested schema NEST that contains table with a rolled up column.
+    MockSchema nestedSchema = new MockSchema("NEST");
+    registerNestedSchema(schema, nestedSchema);
+
+    // Register "EMP_R" table which contains a rolled up column in NEST schema.
+    ImmutableList<String> tablePath =
+        ImmutableList.of(schema.getCatalogName(), schema.name, nestedSchema.name, "EMP_R");
+    final MockTable nestedEmpRolledTable = MockTable.create(this, tablePath, false, 14);
+    nestedEmpRolledTable.addColumn("EMPNO", f.intType, true);
+    nestedEmpRolledTable.addColumn("DEPTNO", f.intType);
+    nestedEmpRolledTable.addColumn("SLACKER", f.booleanType);
+    nestedEmpRolledTable.addColumn("SLACKINGMIN", f.intType);
+    nestedEmpRolledTable.registerRolledUpColumn("SLACKINGMIN");
+    registerTable(nestedEmpRolledTable);
   }
 
   /** Adds some extra tables to the mock catalog. These increase the time and
@@ -622,9 +637,10 @@ public class MockCatalogReader extends CalciteCatalogReader {
 
   private void registerTable(final List<String> names, final Table table) {
     assert names.get(0).equals(DEFAULT_CATALOG);
-    final String schemaName = names.get(1);
-    final String tableName = names.get(2);
-    final CalciteSchema schema = rootSchema.getSubSchema(schemaName, true);
+    final List<String> schemaPath = Util.skipLast(names);
+    final String tableName = Util.last(names);
+    final CalciteSchema schema = SqlValidatorUtil.getSchema(rootSchema,
+        schemaPath, SqlNameMatchers.withCaseSensitive(true));
     schema.add(tableName, table);
   }
 
@@ -632,6 +648,11 @@ public class MockCatalogReader extends CalciteCatalogReader {
     rootSchema.add(schema.name, new AbstractSchema());
   }
 
+  private void registerNestedSchema(MockSchema parentSchema, MockSchema schema) {
+    rootSchema.getSubSchema(parentSchema.getName(), true)
+        .add(schema.name, new AbstractSchema());
+  }
+
   public RelDataType getNamedType(SqlIdentifier typeName) {
     if (typeName.equalsDeep(addressType.getSqlIdentifier(), Litmus.IGNORE)) {
       return addressType;
@@ -829,6 +850,12 @@ public class MockCatalogReader extends CalciteCatalogReader {
     }
 
     public static MockTable create(MockCatalogReader catalogReader,
+        List<String> names, boolean stream, double rowCount) {
+      return new MockTable(catalogReader, names, stream, rowCount, null,
+          NullInitializerExpressionFactory.INSTANCE);
+    }
+
+    public static MockTable create(MockCatalogReader catalogReader,
         MockSchema schema, String name, boolean stream, double rowCount,
         ColumnResolver resolver) {
       return create(catalogReader, schema, name, stream, rowCount, resolver,

http://git-wip-us.apache.org/repos/asf/calcite/blob/26b4712d/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index f62b0aa..c2d89a5 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -10405,6 +10405,9 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
 
     sql("select (((^slackingmin^))) from emp_r")
             .fails(error);
+
+    sql("select ^slackingmin^ from nest.emp_r")
+            .fails(error);
   }
 
   @Test public void testSelectAggregateOnRolledUpColumn() {


[4/4] calcite git commit: [CALCITE-2072] Enable spatial functions by adding 'fun=spatial' to JDBC connect string

Posted by jh...@apache.org.
[CALCITE-2072] Enable spatial functions by adding 'fun=spatial' to JDBC connect string


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

Branch: refs/heads/master
Commit: c1749ade6638cd2e724f33a09f12f45d8d2a5503
Parents: 26b4712
Author: Julian Hyde <jh...@apache.org>
Authored: Fri Dec 8 16:10:55 2017 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Dec 11 14:24:53 2017 -0800

----------------------------------------------------------------------
 .../config/CalciteConnectionConfigImpl.java     | 25 +++++---
 .../config/CalciteConnectionProperty.java       |  3 +-
 .../calcite/prepare/CalciteCatalogReader.java   | 63 +++++++++++++++++---
 .../sql/validate/SqlUserDefinedAggFunction.java |  5 ++
 .../java/org/apache/calcite/test/JdbcTest.java  | 18 ++++++
 site/_docs/adapter.md                           |  2 +-
 site/_docs/howto.md                             | 15 +++++
 site/_docs/spatial.md                           | 18 ++++++
 8 files changed, 131 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
index c2a3239..794d1c8 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionConfigImpl.java
@@ -20,6 +20,8 @@ import org.apache.calcite.avatica.ConnectionConfigImpl;
 import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.avatica.util.Quoting;
 import org.apache.calcite.model.JsonSchema;
+import org.apache.calcite.prepare.CalciteCatalogReader;
+import org.apache.calcite.runtime.GeoFunctions;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.fun.OracleSqlOperatorTable;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -27,8 +29,8 @@ import org.apache.calcite.sql.util.ChainedSqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.sql.validate.SqlConformanceEnum;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.Properties;
 
 /** Implementation of {@link CalciteConnectionConfig}. */
@@ -86,22 +88,29 @@ public class CalciteConnectionConfigImpl extends ConnectionConfigImpl
     if (fun == null || fun.equals("") || fun.equals("standard")) {
       return defaultOperatorTable;
     }
-    final List<SqlOperatorTable> tables = new ArrayList<>();
+    final Collection<SqlOperatorTable> tables = new LinkedHashSet<>();
     for (String s : fun.split(",")) {
-      tables.add(operatorTable(s));
+      operatorTable(s, tables);
     }
+    tables.add(SqlStdOperatorTable.instance());
     return operatorTableClass.cast(
         ChainedSqlOperatorTable.of(
             tables.toArray(new SqlOperatorTable[tables.size()])));
   }
 
-  private static SqlOperatorTable operatorTable(String s) {
+  private static void operatorTable(String s,
+        Collection<SqlOperatorTable> tables) {
     switch (s) {
     case "standard":
-      return SqlStdOperatorTable.instance();
+      tables.add(SqlStdOperatorTable.instance());
+      return;
     case "oracle":
-      return ChainedSqlOperatorTable.of(OracleSqlOperatorTable.instance(),
-          SqlStdOperatorTable.instance());
+      tables.add(OracleSqlOperatorTable.instance());
+      return;
+    case "spatial":
+      tables.add(
+          CalciteCatalogReader.operatorTable(GeoFunctions.class.getName()));
+      return;
     default:
       throw new IllegalArgumentException("Unknown operator table: " + s);
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
index b576aac..67909da 100644
--- a/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
+++ b/core/src/main/java/org/apache/calcite/config/CalciteConnectionProperty.java
@@ -74,7 +74,8 @@ public enum CalciteConnectionProperty implements ConnectionProperty {
   LEX("lex", Type.ENUM, Lex.ORACLE, false),
 
   /** Collection of built-in functions and operators. Valid values include
-   * "standard" and "oracle". */
+   * "standard", "oracle" and "spatial", and also comma-separated lists, for
+   * example "oracle,spatial". */
   FUN("fun", Type.STRING, "standard", true),
 
   /** How identifiers are quoted.

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
index af0d3a6..4dc8be7 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteCatalogReader.java
@@ -19,11 +19,13 @@ package org.apache.calcite.prepare;
 import org.apache.calcite.config.CalciteConnectionConfig;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.model.ModelHandler;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
 import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.runtime.PredicateImpl;
 import org.apache.calcite.schema.AggregateFunction;
 import org.apache.calcite.schema.Function;
@@ -38,14 +40,18 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlOperatorBinding;
+import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.SqlSyntax;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.FamilyOperandTypeChecker;
 import org.apache.calcite.sql.type.InferTypes;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
+import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.util.ListSqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlMoniker;
 import org.apache.calcite.sql.validate.SqlMonikerImpl;
 import org.apache.calcite.sql.validate.SqlMonikerType;
@@ -275,7 +281,43 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
             }));
   }
 
+  /** Creates an operator table that contains functions in the given class.
+   *
+   * @see ModelHandler#addFunctions */
+  public static SqlOperatorTable operatorTable(String className) {
+    // Dummy schema to collect the functions
+    final CalciteSchema schema =
+        CalciteSchema.createRootSchema(false, false);
+    ModelHandler.addFunctions(schema.plus(), null, ImmutableList.<String>of(),
+        className, "*", true);
+
+    // The following is technical debt; see [CALCITE-2082] Remove
+    // RelDataTypeFactory argument from SqlUserDefinedAggFunction constructor
+    final SqlTypeFactoryImpl typeFactory =
+        new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+
+    final ListSqlOperatorTable table = new ListSqlOperatorTable();
+    for (String name : schema.getFunctionNames()) {
+      for (Function function : schema.getFunctions(name, true)) {
+        final SqlIdentifier id = new SqlIdentifier(name, SqlParserPos.ZERO);
+        table.add(
+            toOp(typeFactory, id, function));
+      }
+    }
+    return table;
+  }
+
   private SqlOperator toOp(SqlIdentifier name, final Function function) {
+    return toOp(typeFactory, name, function);
+  }
+
+  /** Converts a function to a {@link org.apache.calcite.sql.SqlOperator}.
+   *
+   * <p>The {@code typeFactory} argument is technical debt; see [CALCITE-2082]
+   * Remove RelDataTypeFactory argument from SqlUserDefinedAggFunction
+   * constructor. */
+  private static SqlOperator toOp(RelDataTypeFactory typeFactory,
+      SqlIdentifier name, final Function function) {
     List<RelDataType> argTypes = new ArrayList<>();
     List<SqlTypeFamily> typeFamilies = new ArrayList<>();
     for (FunctionParameter o : function.getParameters()) {
@@ -292,7 +334,7 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
         };
     final FamilyOperandTypeChecker typeChecker =
         OperandTypes.family(typeFamilies, optional);
-    final List<RelDataType> paramTypes = toSql(argTypes);
+    final List<RelDataType> paramTypes = toSql(typeFactory, argTypes);
     if (function instanceof ScalarFunction) {
       return new SqlUserDefinedFunction(name, infer((ScalarFunction) function),
           InferTypes.explicit(argTypes), typeChecker, paramTypes, function);
@@ -313,9 +355,10 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
     }
   }
 
-  private SqlReturnTypeInference infer(final ScalarFunction function) {
+  private static SqlReturnTypeInference infer(final ScalarFunction function) {
     return new SqlReturnTypeInference() {
       public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+        final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
         final RelDataType type;
         if (function instanceof ScalarFunctionImpl) {
           type = ((ScalarFunctionImpl) function).getReturnType(typeFactory,
@@ -323,30 +366,34 @@ public class CalciteCatalogReader implements Prepare.CatalogReader {
         } else {
           type = function.getReturnType(typeFactory);
         }
-        return toSql(type);
+        return toSql(typeFactory, type);
       }
     };
   }
 
-  private SqlReturnTypeInference infer(final AggregateFunction function) {
+  private static SqlReturnTypeInference infer(
+      final AggregateFunction function) {
     return new SqlReturnTypeInference() {
       public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
+        final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
         final RelDataType type = function.getReturnType(typeFactory);
-        return toSql(type);
+        return toSql(typeFactory, type);
       }
     };
   }
 
-  private List<RelDataType> toSql(List<RelDataType> types) {
+  private static List<RelDataType> toSql(
+      final RelDataTypeFactory typeFactory, List<RelDataType> types) {
     return Lists.transform(types,
         new com.google.common.base.Function<RelDataType, RelDataType>() {
           public RelDataType apply(RelDataType type) {
-            return toSql(type);
+            return toSql(typeFactory, type);
           }
         });
   }
 
-  private RelDataType toSql(RelDataType type) {
+  private static RelDataType toSql(RelDataTypeFactory typeFactory,
+      RelDataType type) {
     if (type instanceof RelDataTypeFactoryImpl.JavaType
         && ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass()
         == Object.class) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
index a8be50b..68c954f 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql.validate;
 
 import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.linq4j.function.Experimental;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
@@ -46,6 +47,10 @@ import java.util.List;
  */
 public class SqlUserDefinedAggFunction extends SqlAggFunction {
   public final AggregateFunction function;
+
+  /** This field is is technical debt; see [CALCITE-2082] Remove
+   * RelDataTypeFactory argument from SqlUserDefinedAggFunction constructor. */
+  @Experimental
   public final RelDataTypeFactory typeFactory;
 
   /** Creates a SqlUserDefinedAggFunction. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index cdebd47..2fd5d0a 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -6157,6 +6157,24 @@ public class JdbcTest {
         .throws_("No match found for function signature NVL(<NUMERIC>, <NUMERIC>)");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-2072">[CALCITE-2072]
+   * Enable spatial operator table by adding 'fun=spatial'to JDBC URL</a>. */
+  @Test public void testFunSpatial() {
+    final String sql = "select distinct\n"
+        + "  ST_PointFromText('POINT(-71.0642.28)') as c\n"
+        + "from \"hr\".\"emps\"";
+    CalciteAssert.that(CalciteAssert.Config.REGULAR)
+        .with("fun", "spatial")
+        .query(sql)
+        .returnsUnordered("C={\"x\":-71.0642,\"y\":0.28}");
+
+    // NVL is present in the Oracle operator table, but not spatial or core
+    CalciteAssert.that(CalciteAssert.Config.REGULAR)
+        .query("select nvl(\"commission\", -99) as c from \"hr\".\"emps\"")
+        .throws_("No match found for function signature NVL(<NUMERIC>, <NUMERIC>)");
+  }
+
   /** Tests that {@link Hook#PARSE_TREE} works. */
   @Test public void testHook() {
     final int[] callCount = {0};

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/site/_docs/adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/adapter.md b/site/_docs/adapter.md
index 5562c4c..35001ed 100644
--- a/site/_docs/adapter.md
+++ b/site/_docs/adapter.md
@@ -88,7 +88,7 @@ as implemented by Avatica's
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#DEFAULT_NULL_COLLATION">defaultNullCollation</a> | How NULL values should be sorted if neither NULLS FIRST nor NULLS LAST are specified in a query. The default, HIGH, sorts NULL values the same as Oracle.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#DRUID_FETCH">druidFetch</a> | How many rows the Druid adapter should fetch at a time when executing SELECT queries.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#FORCE_DECORRELATE">forceDecorrelate</a> | Whether the planner should try de-correlating as much as possible. Default true.
-| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#FUN">fun</a> | Collection of built-in functions and operators. Valid values: "standard" (the default), "oracle".
+| <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#FUN">fun</a> | Collection of built-in functions and operators. Valid values are "standard" (the default), "oracle", "spatial", and may be combined using commas, for example "oracle,spatial".
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#LEX">lex</a> | Lexical policy. Values are ORACLE (default), MYSQL, MYSQL_ANSI, SQL_SERVER, JAVA.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#MATERIALIZATIONS_ENABLED">materializationsEnabled</a> | Whether Calcite should use materializations. Default false.
 | <a href="{{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#MODEL">model</a> | URI of the JSON model file.

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/site/_docs/howto.md
----------------------------------------------------------------------
diff --git a/site/_docs/howto.md b/site/_docs/howto.md
index 742a83f..437f64d 100644
--- a/site/_docs/howto.md
+++ b/site/_docs/howto.md
@@ -464,6 +464,21 @@ Before you start:
   a fix version assigned (most likely the version we are
   just about to release)
 
+Smoke-test `sqlline` with Spatial and Oracle function tables:
+
+{% highlight sql %}
+$ ./sqlline
+> !connect jdbc:calcite:fun=spatial,oracle "sa" ""
+SELECT NVL(ST_Is3D(ST_PointFromText('POINT(-71.064544 42.28787)')), TRUE);
++--------+
+| EXPR$0 |
++--------+
+| false  |
++--------+
+1 row selected (0.039 seconds)
+> !quit
+{% endhighlight %}
+
 Create a release branch named after the release, e.g. `branch-1.1`, and push it to Apache.
 
 {% highlight bash %}

http://git-wip-us.apache.org/repos/asf/calcite/blob/c1749ade/site/_docs/spatial.md
----------------------------------------------------------------------
diff --git a/site/_docs/spatial.md b/site/_docs/spatial.md
index 3b98464..1521ce1 100644
--- a/site/_docs/spatial.md
+++ b/site/_docs/spatial.md
@@ -51,6 +51,24 @@ Calcite's support for spatial data includes:
 
 and will at some point also include query rewrites to use spatial indexes.
 
+## Enabling spatial support
+
+Though the `GEOMETRY` data type is built-in, the functions are not enabled by
+default. You need to add `fun=spatial` to the JDBC connect string to enable
+the functions. For example, `sqlline`:
+
+{% highlight sql %}
+$ ./sqlline
+> !connect jdbc:calcite:fun=spatial "sa" ""
+SELECT ST_PointFromText('POINT(-71.064544 42.28787)');
++-------------------------------+
+| EXPR$0                        |
++-------------------------------+
+| {"x":-71.064544,"y":42.28787} |
++-------------------------------+
+1 row selected (0.323 seconds)
+{% endhighlight %}
+
 ## Acknowledgements
 
 Calcite's OpenGIS implementation uses the