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 2018/06/29 18:42:00 UTC

[4/8] calcite git commit: [CALCITE-531] LATERAL combined with window function or table function

[CALCITE-531] LATERAL combined with window function or table function

Rewrite the mechanism for resolving correlating variables in LATERAL.


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

Branch: refs/heads/master
Commit: 189b50d5ec8ac3c5f61be5540bb186f811fbf633
Parents: aae56a5
Author: Julian Hyde <jh...@apache.org>
Authored: Wed Feb 7 13:46:25 2018 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Fri Jun 29 01:23:21 2018 -0700

----------------------------------------------------------------------
 .../apache/calcite/sql/validate/EmptyScope.java |   4 +-
 .../apache/calcite/sql/validate/JoinScope.java  |   8 ++
 .../sql/validate/SqlUserDefinedTableMacro.java  |   9 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  | 103 ++++++++++++-------
 .../calcite/sql/validate/SqlValidatorScope.java |  15 +++
 .../apache/calcite/sql/validate/TableScope.java |  30 +++---
 .../calcite/sql2rel/SqlToRelConverter.java      |   5 +-
 .../java/org/apache/calcite/test/JdbcTest.java  |  29 ++++++
 .../apache/calcite/test/SqlValidatorTest.java   |  19 +++-
 .../calcite/test/SqlValidatorTestCase.java      |   3 +-
 .../apache/calcite/test/TableFunctionTest.java  |  23 ++++-
 11 files changed, 180 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java b/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
index 77922c7..139fe50 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/EmptyScope.java
@@ -160,13 +160,13 @@ class EmptyScope implements SqlValidatorScope {
           table2 = RelOptTableImpl.create(relOptSchema, rowType, entry, null);
         }
         namespace = new TableNamespace(validator, table2);
-        resolved.found(namespace, false, this, path, remainingNames);
+        resolved.found(namespace, false, null, path, remainingNames);
         return;
       }
       // neither sub-schema nor table
       if (namespace != null
           && !remainingNames.equals(names)) {
-        resolved.found(namespace, false, this, path, remainingNames);
+        resolved.found(namespace, false, null, path, remainingNames);
       }
       return;
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java
index bf40e7d..f1eca33 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java
@@ -92,6 +92,14 @@ public class JoinScope extends ListScope {
   public SqlValidatorScope getUsingScope() {
     return usingScope;
   }
+
+  @Override public boolean isWithin(SqlValidatorScope scope2) {
+    if (this == scope2) {
+      return true;
+    }
+    // go from the JOIN to the enclosing SELECT
+    return usingScope.isWithin(scope2);
+  }
 }
 
 // End JoinScope.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java
index 9ef6cc2..4f191ac 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedTableMacro.java
@@ -117,7 +117,14 @@ public class SqlUserDefinedTableMacro extends SqlFunction {
               + pair.left.getOrdinal() + " (" + pair.left.getName()
               + ") is not literal: " + pair.right);
         }
-        arguments.add(null);
+        final RelDataType type = pair.left.getType(typeFactory);
+        final Object value;
+        if (type.isNullable()) {
+          value = null;
+        } else {
+          value = 0L;
+        }
+        arguments.add(value);
       }
     }
     return arguments;

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/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 27b7f8a..ad1d3af 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
@@ -1888,8 +1888,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
 
     // parse input query
     SqlNode expr = call.getTableRef();
-    SqlNode newExpr = registerFrom(usingScope, matchRecognizeScope, expr,
-        expr, null, null, forceNullable);
+    SqlNode newExpr = registerFrom(usingScope, matchRecognizeScope, true, expr,
+        expr, null, null, forceNullable, false);
     if (expr != newExpr) {
       call.setOperand(0, newExpr);
     }
@@ -1940,6 +1940,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
    * @param parentScope   Parent scope which this scope turns to in order to
    *                      resolve objects
    * @param usingScope    Scope whose child list this scope should add itself to
+   * @param register      Whether to register this scope as a child of
+   *                      {@code usingScope}
    * @param node          Node which namespace is based on
    * @param enclosingNode Outermost node for namespace, including decorations
    *                      such as alias and sample clause
@@ -1947,16 +1949,21 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
    * @param extendList    Definitions of extended columns
    * @param forceNullable Whether to force the type of namespace to be
    *                      nullable because it is in an outer join
+   * @param lateral       Whether LATERAL is specified, so that items to the
+   *                      left of this in the JOIN tree are visible in the
+   *                      scope
    * @return registered node, usually the same as {@code node}
    */
   private SqlNode registerFrom(
       SqlValidatorScope parentScope,
       SqlValidatorScope usingScope,
+      boolean register,
       final SqlNode node,
       SqlNode enclosingNode,
       String alias,
       SqlNodeList extendList,
-      boolean forceNullable) {
+      boolean forceNullable,
+      final boolean lateral) {
     final SqlKind kind = node.getKind();
 
     SqlNode expr;
@@ -2001,6 +2008,21 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       }
     }
 
+    if (lateral) {
+      SqlValidatorScope s = usingScope;
+      while (s instanceof JoinScope) {
+        s = ((JoinScope) s).getUsingScope();
+      }
+      final SqlNode node2 = s != null ? s.getNode() : node;
+      final TableScope tableScope = new TableScope(parentScope, node2);
+      if (usingScope instanceof ListScope) {
+        for (ScopeChild child : ((ListScope) usingScope).children) {
+          tableScope.addChild(child.namespace, child.name, child.nullable);
+        }
+      }
+      parentScope = tableScope;
+    }
+
     SqlCall call;
     SqlNode operand;
     SqlNode newOperand;
@@ -2011,27 +2033,26 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       if (alias == null) {
         alias = call.operand(1).toString();
       }
-      SqlValidatorScope usingScope2 = usingScope;
-      if (call.operandCount() > 2) {
-        usingScope2 = null;
-      }
+      final boolean needAlias = call.operandCount() > 2;
       expr = call.operand(0);
       newExpr =
           registerFrom(
               parentScope,
-              usingScope2,
+              usingScope,
+              !needAlias,
               expr,
               enclosingNode,
               alias,
               extendList,
-              forceNullable);
+              forceNullable,
+              lateral);
       if (newExpr != expr) {
         call.setOperand(0, newExpr);
       }
 
       // If alias has a column list, introduce a namespace to translate
-      // column names.
-      if (call.operandCount() > 2) {
+      // column names. We skipped registering it just now.
+      if (needAlias) {
         registerNamespace(
             usingScope,
             alias,
@@ -2050,11 +2071,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
           registerFrom(
               parentScope,
               usingScope,
+              true,
               expr,
               enclosingNode,
               alias,
               extendList,
-              forceNullable);
+              forceNullable,
+              lateral);
       if (newExpr != expr) {
         call.setOperand(0, newExpr);
       }
@@ -2086,29 +2109,27 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
           registerFrom(
               parentScope,
               joinScope,
+              true,
               left,
               left,
               null,
               null,
-              forceLeftNullable);
+              forceLeftNullable,
+              lateral);
       if (newLeft != left) {
         join.setLeft(newLeft);
       }
-      final SqlValidatorScope rightParentScope;
-      if (rightIsLateral) {
-        rightParentScope = joinScope;
-      } else {
-        rightParentScope = parentScope;
-      }
       final SqlNode newRight =
           registerFrom(
-              rightParentScope,
+              parentScope,
               joinScope,
+              true,
               right,
               right,
               null,
               null,
-              forceRightNullable);
+              forceRightNullable,
+              lateral);
       if (newRight != right) {
         join.setRight(newRight);
       }
@@ -2123,7 +2144,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
           new IdentifierNamespace(
               this, id, extendList, enclosingNode,
               parentScope);
-      registerNamespace(usingScope, alias, newNs, forceNullable);
+      registerNamespace(register ? usingScope : null, alias, newNs,
+          forceNullable);
       if (tableScope == null) {
         tableScope = new TableScope(parentScope, node);
       }
@@ -2134,50 +2156,55 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       return newNode;
 
     case LATERAL:
-      if (tableScope != null) {
-        tableScope.meetLateral();
-      }
       return registerFrom(
           parentScope,
           usingScope,
+          true,
           ((SqlCall) node).operand(0),
           enclosingNode,
           alias,
           extendList,
-          forceNullable);
+          forceNullable,
+          true);
 
     case COLLECTION_TABLE:
       call = (SqlCall) node;
       operand = call.operand(0);
       newOperand =
           registerFrom(
-              tableScope == null ? parentScope : tableScope,
+              parentScope,
               usingScope,
+              register,
               operand,
               enclosingNode,
               alias,
               extendList,
-              forceNullable);
+              forceNullable, lateral);
       if (newOperand != operand) {
         call.setOperand(0, newOperand);
       }
       scopes.put(node, parentScope);
       return newNode;
 
+    case UNNEST:
+      if (!lateral) {
+        return registerFrom(parentScope, usingScope, register, node,
+            enclosingNode, alias, extendList, forceNullable, true);
+      }
+      // fall through
     case SELECT:
     case UNION:
     case INTERSECT:
     case EXCEPT:
     case VALUES:
     case WITH:
-    case UNNEST:
     case OTHER_FUNCTION:
       if (alias == null) {
         alias = deriveAlias(node, nextGeneratedId++);
       }
       registerQuery(
           parentScope,
-          usingScope,
+          register ? usingScope : null,
           node,
           enclosingNode,
           alias,
@@ -2196,18 +2223,20 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
           registerFrom(
               parentScope,
               overScope,
+              true,
               operand,
               enclosingNode,
               alias,
               extendList,
-              forceNullable);
+              forceNullable,
+              lateral);
       if (newOperand != operand) {
         call.setOperand(0, newOperand);
       }
 
       for (ScopeChild child : overScope.children) {
-        registerNamespace(usingScope, child.name, child.namespace,
-            forceNullable);
+        registerNamespace(register ? usingScope : null, child.name,
+            child.namespace, forceNullable);
       }
 
       return newNode;
@@ -2216,11 +2245,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       final SqlCall extend = (SqlCall) node;
       return registerFrom(parentScope,
           usingScope,
+          true,
           extend.getOperandList().get(0),
           extend,
           alias,
           (SqlNodeList) extend.getOperandList().get(1),
-          forceNullable);
+          forceNullable,
+          lateral);
 
     default:
       throw Util.unexpected(kind);
@@ -2355,10 +2386,12 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
             registerFrom(
                 parentScope,
                 selectScope,
+                true,
                 from,
                 from,
                 null,
                 null,
+                false,
                 false);
         if (newFrom != from) {
           select.setFrom(newFrom);
@@ -3720,7 +3753,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     // We've found a table. But is it a sequence?
     final SqlValidatorNamespace ns = resolved.only().namespace;
     if (ns instanceof TableNamespace) {
-      final Table table = ((RelOptTable) ns.getTable()).unwrap(Table.class);
+      final Table table = ns.getTable().unwrap(Table.class);
       switch (table.getJdbcTableType()) {
       case SEQUENCE:
       case TEMPORARY_SEQUENCE:

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
index c8eb6b0..b32ed36 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlIdentifier;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlWindow;
 import org.apache.calcite.util.Pair;
 
@@ -202,6 +203,12 @@ public interface SqlValidatorScope {
    * warrants it. */
   RelDataType nullifyType(SqlNode node, RelDataType type);
 
+  /** Returns whether this scope is enclosed within {@code scope2} in such
+   * a way that it can see the contents of {@code scope2}. */
+  default boolean isWithin(SqlValidatorScope scope2)  {
+    return this == scope2;
+  }
+
   /** Callback from {@link SqlValidatorScope#resolve}. */
   interface Resolved {
     void found(SqlValidatorNamespace namespace, boolean nullable,
@@ -244,6 +251,10 @@ public interface SqlValidatorScope {
 
     protected void build(ImmutableList.Builder<Step> paths) {
     }
+
+    @Override public String toString() {
+      return stepNames().toString();
+    }
   }
 
   /** A path that has no steps. */
@@ -284,6 +295,9 @@ public interface SqlValidatorScope {
 
     public void found(SqlValidatorNamespace namespace, boolean nullable,
         SqlValidatorScope scope, Path path, List<String> remainingNames) {
+      if (scope instanceof TableScope) {
+        scope = scope.getValidator().getSelectScope((SqlSelect) scope.getNode());
+      }
       resolves.add(
           new Resolve(namespace, nullable, scope, path, remainingNames));
     }
@@ -317,6 +331,7 @@ public interface SqlValidatorScope {
       this.namespace = Preconditions.checkNotNull(namespace);
       this.nullable = nullable;
       this.scope = scope;
+      assert !(scope instanceof TableScope);
       this.path = Preconditions.checkNotNull(path);
       this.remainingNames = remainingNames == null ? ImmutableList.<String>of()
           : ImmutableList.copyOf(remainingNames);

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java b/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java
index aa81d41..89171c2 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java
@@ -17,6 +17,9 @@
 package org.apache.calcite.sql.validate;
 
 import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlSelect;
+
+import com.google.common.base.Preconditions;
 
 /**
  * The name-resolution scope of a LATERAL TABLE clause.
@@ -29,39 +32,30 @@ class TableScope extends ListScope {
 
   private final SqlNode node;
 
-  // The expression inside the LATERAL can only see tables before it in the FROM clause.
-  // We use this flag to indicate whether current table is before LATERAL.
-  private boolean beforeLateral;
-
   //~ Constructors -----------------------------------------------------------
 
   /**
    * Creates a scope corresponding to a LATERAL TABLE clause.
    *
-   * @param parent   Parent scope, or null
+   * @param parent  Parent scope
    */
   TableScope(SqlValidatorScope parent, SqlNode node) {
-    super(parent);
-    this.node = node;
-    this.beforeLateral = true;
+    super(Preconditions.checkNotNull(parent));
+    this.node = Preconditions.checkNotNull(node);
   }
 
   //~ Methods ----------------------------------------------------------------
 
-
-  @Override public void addChild(SqlValidatorNamespace ns, String alias,
-      boolean nullable) {
-    if (beforeLateral) {
-      super.addChild(ns, alias, nullable);
-    }
-  }
-
   public SqlNode getNode() {
     return node;
   }
 
-  public void meetLateral() {
-    this.beforeLateral = false;
+  @Override public boolean isWithin(SqlValidatorScope scope2) {
+    if (this == scope2) {
+      return true;
+    }
+    SqlValidatorScope s = getValidator().getSelectScope((SqlSelect) node);
+    return s.isWithin(scope2);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/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 71edcee..3767e30 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -2439,7 +2439,7 @@ public class SqlToRelConverter {
       String originalFieldName = fieldAccess.getField().getName();
 
       final SqlNameMatcher nameMatcher =
-          lookup.bb.scope.getValidator().getCatalogReader().nameMatcher();
+          bb.getValidator().getCatalogReader().nameMatcher();
       final SqlValidatorScope.ResolvedImpl resolved =
           new SqlValidatorScope.ResolvedImpl();
       lookup.bb.scope.resolve(ImmutableList.of(originalRelName),
@@ -2450,7 +2450,7 @@ public class SqlToRelConverter {
       final RelDataType rowType = resolve.rowType();
       final int childNamespaceIndex = resolve.path.steps().get(0).i;
       final SqlValidatorScope ancestorScope = resolve.scope;
-      boolean correlInCurrentScope = ancestorScope == bb.scope;
+      boolean correlInCurrentScope = bb.scope.isWithin(ancestorScope);
 
       if (!correlInCurrentScope) {
         continue;
@@ -5404,6 +5404,7 @@ public class SqlToRelConverter {
   private static class CorrelationUse {
     private final CorrelationId id;
     private final ImmutableBitSet requiredColumns;
+    /** The relational expression that uses the variable. */
     private final RelNode r;
 
     CorrelationUse(CorrelationId id, ImmutableBitSet requiredColumns,

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/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 b68f2a8..40d2f37 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2111,6 +2111,35 @@ public class JdbcTest {
             "empid=150; deptno=10; name=Sebastian; salary=7000.0; commission=null; deptno0=10; name0=Sales; employees=[{100, 10, Bill, 10000.0, 1000}, {150, 10, Sebastian, 7000.0, null}]; location={-122, 38}");
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-531">[CALCITE-531]
+   * Window function does not work in LATERAL</a>. */
+  @Test public void testLateralWithOver() {
+    final String sql = "select \"emps\".\"name\", d.\"deptno\", d.m\n"
+        + "from \"hr\".\"emps\",\n"
+        + "  LATERAL (\n"
+        + "    select \"depts\".\"deptno\",\n"
+        + "      max(\"deptno\" + \"emps\".\"empid\") over (\n"
+        + "        partition by \"emps\".\"deptno\") as m\n"
+        + "     from \"hr\".\"depts\"\n"
+        + "     where \"emps\".\"deptno\" = \"depts\".\"deptno\") as d";
+    CalciteAssert.that()
+        .with(CalciteAssert.Config.REGULAR)
+        .query(sql)
+        .returnsUnordered("name=Bill; deptno=10; M=190",
+            "name=Bill; deptno=30; M=190",
+            "name=Bill; deptno=40; M=190",
+            "name=Eric; deptno=10; M=240",
+            "name=Eric; deptno=30; M=240",
+            "name=Eric; deptno=40; M=240",
+            "name=Sebastian; deptno=10; M=190",
+            "name=Sebastian; deptno=30; M=190",
+            "name=Sebastian; deptno=40; M=190",
+            "name=Theodore; deptno=10; M=190",
+            "name=Theodore; deptno=30; M=190",
+            "name=Theodore; deptno=40; M=190");
+  }
+
   /** Per SQL std, UNNEST is implicitly LATERAL. */
   @Test public void testUnnestArrayColumn() {
     CalciteAssert.hr()

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/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 743b7c4..d710661 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4812,8 +4812,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         "RecordType(CHAR(2) NOT NULL A, INTEGER NOT NULL B) NOT NULL");
   }
 
-  // todo: implement IN
-  public void _testAmbiguousColumnInIn() {
+  @Test public void testAmbiguousColumnInIn() {
     // ok: cyclic reference
     check("select * from emp as e\n"
         + "where e.deptno in (\n"
@@ -7506,7 +7505,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
   }
 
   @Test public void testUnnestArrayColumn() {
-    final String sql = "select d.deptno, e.*\n"
+    final String sql1 = "select d.deptno, e.*\n"
         + "from dept_nested as d,\n"
         + " UNNEST(d.employees) as e";
     final String type = "RecordType(INTEGER NOT NULL DEPTNO,"
@@ -7515,7 +7514,13 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         + " RecordType(RecordType(VARCHAR(10) NOT NULL TYPE, VARCHAR(20) NOT NULL DESC,"
         + " RecordType(VARCHAR(10) NOT NULL A, VARCHAR(10) NOT NULL B) NOT NULL OTHERS)"
         + " NOT NULL ARRAY NOT NULL SKILLS) NOT NULL DETAIL) NOT NULL";
-    sql(sql).type(type);
+    sql(sql1).type(type);
+
+    // equivalent query without table alias
+    final String sql1b = "select d.deptno, e.*\n"
+        + "from dept_nested as d,\n"
+        + " UNNEST(employees) as e";
+    sql(sql1b).type(type);
 
     // equivalent query using CROSS JOIN
     final String sql2 = "select d.deptno, e.*\n"
@@ -7523,6 +7528,12 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         + " UNNEST(d.employees) as e";
     sql(sql2).type(type);
 
+    // equivalent query using CROSS JOIN, without table alias
+    final String sql2b = "select d.deptno, e.*\n"
+        + "from dept_nested as d CROSS JOIN\n"
+        + " UNNEST(employees) as e";
+    sql(sql2b).type(type);
+
     // LATERAL works left-to-right
     final String sql3 = "select d.deptno, e.*\n"
         + "from UNNEST(^d^.employees) as e, dept_nested as d";

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
index 77e8753..b78df79 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTestCase.java
@@ -335,7 +335,8 @@ public class SqlValidatorTestCase {
             actualLine = Integer.parseInt(matcher.group(1));
             actualColumn = Integer.parseInt(matcher.group(2));
           } else {
-            if (actualMessage.toString().matches(expectedMsgPattern)) {
+            if (expectedMsgPattern != null
+                && actualMessage.matches(expectedMsgPattern)) {
               return;
             }
           }

http://git-wip-us.apache.org/repos/asf/calcite/blob/189b50d5/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java b/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
index 336fb54..b0ed547 100644
--- a/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TableFunctionTest.java
@@ -413,13 +413,26 @@ public class TableFunctionTest {
   }
 
   @Test public void testCrossApply() {
-    final String q = "select *\n"
+    final String q1 = "select *\n"
         + "from (values 2, 5) as t (c)\n"
         + "cross apply table(\"s\".\"fibonacci2\"(c))";
-    with()
-        .with(CalciteConnectionProperty.CONFORMANCE, SqlConformanceEnum.LENIENT)
-        .query(q)
-        .returnsUnordered("C=7");
+    final String q2 = "select *\n"
+        + "from (values 2, 5) as t (c)\n"
+        + "cross apply table(\"s\".\"fibonacci2\"(t.c))";
+    for (String q : new String[] {q1, q2}) {
+      with()
+          .with(CalciteConnectionProperty.CONFORMANCE,
+              SqlConformanceEnum.LENIENT)
+          .query(q)
+          .returnsUnordered("C=2; N=1",
+              "C=2; N=1",
+              "C=2; N=2",
+              "C=5; N=1",
+              "C=5; N=1",
+              "C=5; N=2",
+              "C=5; N=3",
+              "C=5; N=5");
+    }
   }
 }