You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jc...@apache.org on 2017/04/28 09:56:47 UTC

calcite git commit: [CALCITE-1767] Fix join/aggregate rewriting rule when same table is referenced more than once

Repository: calcite
Updated Branches:
  refs/heads/master d2368327e -> 6f07293a3


[CALCITE-1767] Fix join/aggregate rewriting rule when same table is referenced more than once


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

Branch: refs/heads/master
Commit: 6f07293a3843c8790af9c028b2962fd8a0512db9
Parents: d236832
Author: Jesus Camacho Rodriguez <jc...@apache.org>
Authored: Fri Apr 28 10:53:30 2017 +0100
Committer: Jesus Camacho Rodriguez <jc...@apache.org>
Committed: Fri Apr 28 10:53:50 2017 +0100

----------------------------------------------------------------------
 .../rel/metadata/RelMdAllPredicates.java        | 131 +++++++++++++++----
 .../rel/rules/AbstractMaterializedViewRule.java |  54 ++++----
 .../apache/calcite/rex/RexTableInputRef.java    |   6 +-
 .../java/org/apache/calcite/rex/RexUtil.java    |  16 +++
 .../calcite/test/MaterializationTest.java       |  42 ++++++
 .../apache/calcite/test/RelMetadataTest.java    |  55 ++++++++
 6 files changed, 255 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/6f07293a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
index 616c429..f5520cb 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
@@ -35,12 +35,23 @@ import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexTableInputRef;
+import org.apache.calcite.rex.RexTableInputRef.RelTableRef;
+import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Function;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -152,10 +163,8 @@ public class RelMdAllPredicates
 
     final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
     final RexNode pred = join.getCondition();
-    final RelNode leftInput = join.getLeft();
-    final RelNode rightInput = join.getRight();
-    final int nLeftColumns = leftInput.getRowType().getFieldList().size();
 
+    final Multimap<List<String>, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
     RelOptPredicateList newPreds = RelOptPredicateList.EMPTY;
     for (RelNode input : join.getInputs()) {
       final RelOptPredicateList inputPreds = mq.getAllPredicates(input);
@@ -163,7 +172,45 @@ public class RelMdAllPredicates
         // Bail out
         return null;
       }
-      newPreds = newPreds.union(rexBuilder, inputPreds);
+      // If it does not contain table references, nothing needs to be done
+      if (!RexUtil.containsTableInputRef(inputPreds.pulledUpPredicates)) {
+        newPreds = newPreds.union(rexBuilder, inputPreds);
+        continue;
+      }
+      // Gather table references
+      final Set<RelTableRef> tableRefs = mq.getTableReferences(input);
+      if (input == join.getLeft()) {
+        // Left input references remain unchanged
+        for (RelTableRef leftRef : tableRefs) {
+          qualifiedNamesToRefs.put(leftRef.getQualifiedName(), leftRef);
+        }
+        newPreds = newPreds.union(rexBuilder, inputPreds);
+      } else {
+        // Right input references might need to be updated if there are table name
+        // clashes with left input
+        final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
+        for (RelTableRef rightRef : tableRefs) {
+          int shift = 0;
+          Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
+              rightRef.getQualifiedName());
+          if (lRefs != null) {
+            shift = lRefs.size();
+          }
+          currentTablesMapping.put(rightRef,
+              RelTableRef.of(rightRef.getTable(), shift + rightRef.getEntityNumber()));
+        }
+        final List<RexNode> updatedPreds = Lists.newArrayList(
+            Iterables.transform(
+                inputPreds.pulledUpPredicates,
+                new Function<RexNode, RexNode>() {
+                  @Override public RexNode apply(RexNode e) {
+                    return RexUtil.swapTableReferences(rexBuilder, e, currentTablesMapping);
+                  }
+                }
+          ));
+        newPreds = newPreds.union(rexBuilder,
+            RelOptPredicateList.of(rexBuilder, updatedPreds));
+      }
     }
 
     // Extract input fields referenced by Join condition
@@ -175,27 +222,14 @@ public class RelMdAllPredicates
     // Infer column origin expressions for given references
     final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
     for (int idx : inputFieldsUsed) {
-      if (idx < nLeftColumns) {
-        final RexInputRef inputRef = RexInputRef.of(idx, leftInput.getRowType().getFieldList());
-        final Set<RexNode> originalExprs = mq.getExpressionLineage(leftInput, inputRef);
-        if (originalExprs == null) {
-          // Bail out
-          return null;
-        }
-        final RexInputRef ref = RexInputRef.of(idx, join.getRowType().getFieldList());
-        mapping.put(ref, originalExprs);
-      } else {
-        // Right input.
-        final RexInputRef inputRef = RexInputRef.of(idx - nLeftColumns,
-                rightInput.getRowType().getFieldList());
-        final Set<RexNode> originalExprs = mq.getExpressionLineage(rightInput, inputRef);
-        if (originalExprs == null) {
-          // Bail out
-          return null;
-        }
-        final RexInputRef ref = RexInputRef.of(idx, join.getRowType().getFieldList());
-        mapping.put(ref, originalExprs);
+      final RexInputRef inputRef = RexInputRef.of(idx, join.getRowType().getFieldList());
+      final Set<RexNode> originalExprs = mq.getExpressionLineage(join, inputRef);
+      if (originalExprs == null) {
+        // Bail out
+        return null;
       }
+      final RexInputRef ref = RexInputRef.of(idx, join.getRowType().getFieldList());
+      mapping.put(ref, originalExprs);
     }
 
     // Replace with new expressions and return union of predicates
@@ -217,14 +251,59 @@ public class RelMdAllPredicates
   public RelOptPredicateList getAllPredicates(Union union, RelMetadataQuery mq) {
     final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
 
+    final Multimap<List<String>, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
     RelOptPredicateList newPreds = RelOptPredicateList.EMPTY;
-    for (RelNode input : union.getInputs()) {
+    for (int i = 0; i < union.getInputs().size(); i++) {
+      final RelNode input = union.getInput(i);
       final RelOptPredicateList inputPreds = mq.getAllPredicates(input);
       if (inputPreds == null) {
         // Bail out
         return null;
       }
-      newPreds = newPreds.union(rexBuilder, inputPreds);
+      // If it does not contain table references, nothing needs to be done
+      if (!RexUtil.containsTableInputRef(inputPreds.pulledUpPredicates)) {
+        newPreds = newPreds.union(rexBuilder, inputPreds);
+        continue;
+      }
+      // Gather table references
+      final Set<RelTableRef> tableRefs = mq.getTableReferences(input);
+      if (i == 0) {
+        // Left input references remain unchanged
+        for (RelTableRef leftRef : tableRefs) {
+          qualifiedNamesToRefs.put(leftRef.getQualifiedName(), leftRef);
+        }
+        newPreds = newPreds.union(rexBuilder, inputPreds);
+      } else {
+        // Right input references might need to be updated if there are table name
+        // clashes with left input
+        final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
+        for (RelTableRef rightRef : tableRefs) {
+          int shift = 0;
+          Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
+              rightRef.getQualifiedName());
+          if (lRefs != null) {
+            shift = lRefs.size();
+          }
+          currentTablesMapping.put(rightRef,
+              RelTableRef.of(rightRef.getTable(), shift + rightRef.getEntityNumber()));
+        }
+        // Add to existing qualified names
+        for (RelTableRef newRef : currentTablesMapping.values()) {
+          qualifiedNamesToRefs.put(newRef.getQualifiedName(), newRef);
+        }
+        // Update preds
+        final List<RexNode> updatedPreds = Lists.newArrayList(
+            Iterables.transform(
+                inputPreds.pulledUpPredicates,
+                new Function<RexNode, RexNode>() {
+                  @Override public RexNode apply(RexNode e) {
+                    return RexUtil.swapTableReferences(rexBuilder, e, currentTablesMapping);
+                  }
+                }
+          ));
+        newPreds = newPreds.union(rexBuilder,
+            RelOptPredicateList.of(rexBuilder, updatedPreds));
+      }
     }
     return newPreds;
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/6f07293a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index 2893145..54586c7 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -298,14 +298,15 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             matchModality = MatchModality.COMPLETE;
           }
 
-          // 4. We map every table in the query to a view table with the same qualified
-          // name.
+          // 4. We map every table in the query to a table with the same qualified
+          // name (all query tables are contained in the view, thus this is equivalent
+          // to mapping every table in the query to a view table).
           final Multimap<RelTableRef, RelTableRef> multiMapTables = ArrayListMultimap.create();
-          for (RelTableRef queryTableRef : queryTableRefs) {
-            for (RelTableRef viewTableRef : viewTableRefs) {
-              if (queryTableRef.getQualifiedName().equals(
-                  viewTableRef.getQualifiedName())) {
-                multiMapTables.put(queryTableRef, viewTableRef);
+          for (RelTableRef queryTableRef1 : queryTableRefs) {
+            for (RelTableRef queryTableRef2 : queryTableRefs) {
+              if (queryTableRef1.getQualifiedName().equals(
+                  queryTableRef2.getQualifiedName())) {
+                multiMapTables.put(queryTableRef1, queryTableRef2);
               }
             }
           }
@@ -938,30 +939,35 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
    */
   private static List<BiMap<RelTableRef, RelTableRef>> generateTableMappings(
       Multimap<RelTableRef, RelTableRef> multiMapTables) {
-    final List<BiMap<RelTableRef, RelTableRef>> result = new ArrayList<>();
     if (multiMapTables.isEmpty()) {
-      return result;
+      return ImmutableList.of();
     }
-    result.add(HashBiMap.<RelTableRef, RelTableRef>create());
+    List<BiMap<RelTableRef, RelTableRef>> result =
+        ImmutableList.<BiMap<RelTableRef, RelTableRef>>of(
+            HashBiMap.<RelTableRef, RelTableRef>create());
     for (Entry<RelTableRef, Collection<RelTableRef>> e : multiMapTables.asMap().entrySet()) {
-      boolean added = false;
+      if (e.getValue().size() == 1) {
+        // Only one reference, we can just add it to every map
+        RelTableRef target = e.getValue().iterator().next();
+        for (BiMap<RelTableRef, RelTableRef> m : result) {
+          m.put(e.getKey(), target);
+        }
+        continue;
+      }
+      // Multiple references: flatten
+      ImmutableList.Builder<BiMap<RelTableRef, RelTableRef>> newResult =
+          ImmutableList.builder();
       for (RelTableRef target : e.getValue()) {
-        if (added) {
-          for (BiMap<RelTableRef, RelTableRef> m : result) {
+        for (BiMap<RelTableRef, RelTableRef> m : result) {
+          if (!m.containsValue(target)) {
             final BiMap<RelTableRef, RelTableRef> newM =
                 HashBiMap.<RelTableRef, RelTableRef>create(m);
             newM.put(e.getKey(), target);
-            result.add(newM);
+            newResult.add(newM);
           }
-        } else {
-          for (BiMap<RelTableRef, RelTableRef> m : result) {
-            m.put(e.getKey(), target);
-          }
-          added = true;
         }
       }
-      // Mapping needs to exist
-      assert added;
+      result = newResult.build();
     }
     return result;
   }
@@ -1103,6 +1109,7 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
             RexTableInputRef uniqueKeyColumnRef = RexTableInputRef.of(parentTRef, uniqueKeyPos,
                 parentTRef.getTable().getRowType().getFieldList().get(uniqueKeyPos).getType());
             if (!foreignKeyColumnType.isNullable()
+                && vEC.getEquivalenceClassesMap().containsKey(uniqueKeyColumnRef)
                 && vEC.getEquivalenceClassesMap().get(uniqueKeyColumnRef).contains(
                     foreignKeyColumnRef)) {
               equiColumns.put(foreignKeyColumnRef, uniqueKeyColumnRef);
@@ -1118,7 +1125,6 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
               edge = graph.addEdge(tRef, parentTRef);
             }
             edge.equiColumns.putAll(equiColumns);
-            break;
           }
         }
       }
@@ -1508,6 +1514,10 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule {
     public Edge(RelTableRef source, RelTableRef target) {
       super(source, target);
     }
+
+    public String toString() {
+      return "{" + source + " -> " + target + "}";
+    }
   }
 
   /** Complete, view partial, or query partial. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/6f07293a/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java b/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
index 83a3411..20762c1 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
@@ -95,7 +95,7 @@ public class RexTableInputRef extends RexInputRef {
   }
 
   /** Identifies uniquely a table by its qualified name and its entity number (occurrence) */
-  public static class RelTableRef {
+  public static class RelTableRef implements Comparable<RelTableRef> {
 
     private final RelOptTable table;
     private final int entityNumber;
@@ -139,6 +139,10 @@ public class RexTableInputRef extends RexInputRef {
     public static RelTableRef of(RelOptTable table, int entityNumber) {
       return new RelTableRef(table, entityNumber);
     }
+
+    @Override public int compareTo(RelTableRef o) {
+      return digest.compareTo(o.digest);
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/6f07293a/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 8b3c989..6ade754 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -828,6 +828,22 @@ public class RexUtil {
   }
 
   /**
+   * Returns whether any of the given expression trees contains a
+   * {link RexTableInputRef} node.
+   *
+   * @param nodes a list of RexNode trees
+   * @return true if at least one was found, otherwise false
+   */
+  public static boolean containsTableInputRef(List<RexNode> nodes) {
+    for (RexNode e : nodes) {
+      if (containsTableInputRef(e) != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * Returns whether a given tree contains any {link RexTableInputRef} nodes.
    *
    * @param node a RexNode tree

http://git-wip-us.apache.org/repos/asf/calcite/blob/6f07293a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
index 49c88f5..eb15b5b 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -1451,6 +1451,48 @@ public class MaterializationTest {
               + "  EnumerableTableScan(table=[[hr, m0]])"));
   }
 
+  @Test public void testJoinMaterializationUKFK6() {
+    checkMaterialize(
+      "select \"emps\".\"empid\", \"emps\".\"deptno\" from \"emps\"\n"
+          + "join \"depts\" \"a\" on (\"emps\".\"deptno\"=\"a\".\"deptno\")\n"
+          + "join \"depts\" \"b\" on (\"emps\".\"deptno\"=\"b\".\"deptno\")\n"
+          + "join \"dependents\" using (\"empid\")"
+          + "where \"emps\".\"empid\" = 1",
+      "select \"emps\".\"empid\" from \"emps\"\n"
+          + "join \"dependents\" using (\"empid\")\n"
+          + "where \"emps\".\"empid\" = 1",
+      HR_FKUK_MODEL,
+      CalciteAssert.checkResultContains(
+          "EnumerableCalc(expr#0..1=[{inputs}], empid=[$t0])\n"
+              + "  EnumerableTableScan(table=[[hr, m0]])"));
+  }
+
+  @Test public void testJoinMaterializationUKFK7() {
+    checkNoMaterialize(
+      "select \"emps\".\"empid\", \"emps\".\"deptno\" from \"emps\"\n"
+          + "join \"depts\" \"a\" on (\"emps\".\"name\"=\"a\".\"name\")\n"
+          + "join \"depts\" \"b\" on (\"emps\".\"name\"=\"b\".\"name\")\n"
+          + "join \"dependents\" using (\"empid\")"
+          + "where \"emps\".\"empid\" = 1",
+      "select \"emps\".\"empid\" from \"emps\"\n"
+          + "join \"dependents\" using (\"empid\")\n"
+          + "where \"emps\".\"empid\" = 1",
+      HR_FKUK_MODEL);
+  }
+
+  @Test public void testJoinMaterializationUKFK8() {
+    checkNoMaterialize(
+      "select \"emps\".\"empid\", \"emps\".\"deptno\" from \"emps\"\n"
+          + "join \"depts\" \"a\" on (\"emps\".\"deptno\"=\"a\".\"deptno\")\n"
+          + "join \"depts\" \"b\" on (\"emps\".\"name\"=\"b\".\"name\")\n"
+          + "join \"dependents\" using (\"empid\")"
+          + "where \"emps\".\"empid\" = 1",
+      "select \"emps\".\"empid\" from \"emps\"\n"
+          + "join \"dependents\" using (\"empid\")\n"
+          + "where \"emps\".\"empid\" = 1",
+      HR_FKUK_MODEL);
+  }
+
   @Test public void testSubQuery() {
     String q = "select \"empid\", \"deptno\", \"salary\" from \"emps\" e1\n"
         + "where \"empid\" = (\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/6f07293a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index af25b30..79b985a 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -75,6 +75,7 @@ import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexTableInputRef;
+import org.apache.calcite.rex.RexTableInputRef.RelTableRef;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -90,6 +91,7 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
 
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.CustomTypeSafeMatcher;
@@ -1881,6 +1883,59 @@ public class RelMetadataTest extends SqlToRelTestBase {
     assertNull(inputSet);
   }
 
+  @Test public void testAllPredicatesAndTablesJoin() {
+    final String sql = "select x.sal, y.deptno from\n"
+        + "(select a.deptno, c.sal from (select * from emp limit 7) as a\n"
+        + "cross join (select * from dept limit 1) as b\n"
+        + "inner join (select * from emp limit 2) as c\n"
+        + "on a.deptno = c.deptno) as x\n"
+        + "inner join\n"
+        + "(select a.deptno, c.sal from (select * from emp limit 7) as a\n"
+        + "cross join (select * from dept limit 1) as b\n"
+        + "inner join (select * from emp limit 2) as c\n"
+        + "on a.deptno = c.deptno) as y\n"
+        + "on x.deptno = y.deptno";
+    final RelNode rel = convertSql(sql);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelOptPredicateList inputSet = mq.getAllPredicates(rel);
+    assertThat(inputSet.pulledUpPredicates.toString(),
+        equalTo("[true, "
+            + "=([CATALOG, SALES, EMP].#0.$7, [CATALOG, SALES, EMP].#1.$7), "
+            + "true, "
+            + "=([CATALOG, SALES, EMP].#2.$7, [CATALOG, SALES, EMP].#3.$7), "
+            + "=([CATALOG, SALES, EMP].#0.$7, [CATALOG, SALES, EMP].#2.$7)]"));
+    final Set<RelTableRef> tableReferences = Sets.newTreeSet(mq.getTableReferences(rel));
+    assertThat(tableReferences.toString(),
+        equalTo("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, DEPT].#1, "
+            + "[CATALOG, SALES, EMP].#0, [CATALOG, SALES, EMP].#1, "
+            + "[CATALOG, SALES, EMP].#2, [CATALOG, SALES, EMP].#3]"));
+  }
+
+  @Test public void testAllPredicatesAndTableUnion() {
+    final String sql = "select a.deptno, c.sal from (select * from emp limit 7) as a\n"
+        + "cross join (select * from dept limit 1) as b\n"
+        + "inner join (select * from emp limit 2) as c\n"
+        + "on a.deptno = c.deptno\n"
+        + "union all\n"
+        + "select a.deptno, c.sal from (select * from emp limit 7) as a\n"
+        + "cross join (select * from dept limit 1) as b\n"
+        + "inner join (select * from emp limit 2) as c\n"
+        + "on a.deptno = c.deptno";
+    final RelNode rel = convertSql(sql);
+    final RelMetadataQuery mq = RelMetadataQuery.instance();
+    final RelOptPredicateList inputSet = mq.getAllPredicates(rel);
+    assertThat(inputSet.pulledUpPredicates.toString(),
+        equalTo("[true, "
+            + "=([CATALOG, SALES, EMP].#0.$7, [CATALOG, SALES, EMP].#1.$7), "
+            + "true, "
+            + "=([CATALOG, SALES, EMP].#2.$7, [CATALOG, SALES, EMP].#3.$7)]"));
+    final Set<RelTableRef> tableReferences = Sets.newTreeSet(mq.getTableReferences(rel));
+    assertThat(tableReferences.toString(),
+        equalTo("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, DEPT].#1, "
+            + "[CATALOG, SALES, EMP].#0, [CATALOG, SALES, EMP].#1, "
+            + "[CATALOG, SALES, EMP].#2, [CATALOG, SALES, EMP].#3]"));
+  }
+
   private void checkNodeTypeCount(String sql, Map<Class<? extends RelNode>, Integer> expected) {
     final RelNode rel = convertSql(sql);
     final RelMetadataQuery mq = RelMetadataQuery.instance();