This is an automated email from the ASF dual-hosted git repository.

mihaibudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 2d28ce3244 [CALCITE-7825] RelMdTableReferences does not account for 
tables in RexSubQuery expressions or Correlate
2d28ce3244 is described below

commit 2d28ce3244dd9799909c44b1fa8a2fb4e4eda673
Author: Dongsheng He <[email protected]>
AuthorDate: Mon Sep 28 13:56:51 2026 +0800

    [CALCITE-7825] RelMdTableReferences does not account for tables in 
RexSubQuery expressions or Correlate
---
 .../calcite/rel/metadata/RelMdTableReferences.java | 166 ++++++++++++++-------
 .../main/java/org/apache/calcite/rex/RexUtil.java  |  32 +++-
 .../org/apache/calcite/test/RelMetadataTest.java   | 153 +++++++++++++++++++
 .../GeneratedMetadata_TableReferencesHandler.java  |  18 +++
 4 files changed, 309 insertions(+), 60 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java
index 96691863f4..3d1796eb67 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdTableReferences.java
@@ -20,26 +20,40 @@
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.core.Collect;
+import org.apache.calcite.rel.core.Combine;
+import org.apache.calcite.rel.core.Correlate;
 import org.apache.calcite.rel.core.Exchange;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.Match;
 import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.RepeatUnion;
 import org.apache.calcite.rel.core.Sample;
 import org.apache.calcite.rel.core.SetOp;
+import org.apache.calcite.rel.core.Snapshot;
 import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.Spool;
 import org.apache.calcite.rel.core.TableFunctionScan;
 import org.apache.calcite.rel.core.TableModify;
 import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.core.Uncollect;
+import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rel.core.Window;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexSubQuery;
 import org.apache.calcite.rex.RexTableInputRef.RelTableRef;
+import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -101,6 +115,11 @@ public Set<RelTableRef> getTableReferences(TableScan rel, 
RelMetadataQuery mq) {
     return ImmutableSet.of(RelTableRef.of(rel.getTable(), 0));
   }
 
+  /** Table references from Values. */
+  public Set<RelTableRef> getTableReferences(Values rel, RelMetadataQuery mq) {
+    return ImmutableSet.of();
+  }
+
   /**
    * Table references from Aggregate.
    */
@@ -112,45 +131,23 @@ public Set<RelTableRef> getTableReferences(TableScan rel, 
RelMetadataQuery mq) {
    * Table references from Join.
    */
   public @Nullable Set<RelTableRef> getTableReferences(Join rel, 
RelMetadataQuery mq) {
-    final RelNode leftInput = rel.getLeft();
-    final RelNode rightInput = rel.getRight();
-    final Set<RelTableRef> result = new HashSet<>();
+    return getTableReferences(ImmutableList.of(rel.getLeft(), rel.getRight()),
+        ImmutableList.of(rel.getCondition()), mq);
+  }
 
-    // Gather table references, left input references remain unchanged
-    final Multimap<List<String>, RelTableRef> leftQualifiedNamesToRefs = 
HashMultimap.create();
-    final Set<RelTableRef> leftTableRefs = mq.getTableReferences(leftInput);
-    if (leftTableRefs == null) {
-      // We could not infer the table refs from left input
-      return null;
-    }
-    for (RelTableRef leftRef : leftTableRefs) {
-      assert !result.contains(leftRef);
-      result.add(leftRef);
-      leftQualifiedNamesToRefs.put(leftRef.getQualifiedName(), leftRef);
-    }
+  /** Table references from both inputs of Correlate. */
+  public @Nullable Set<RelTableRef> getTableReferences(Correlate rel, 
RelMetadataQuery mq) {
+    return getTableReferences(rel.getInputs(), ImmutableList.of(), mq);
+  }
 
-    // Gather table references, right input references might need to be
-    // updated if there are table names clashes with left input
-    final Set<RelTableRef> rightTableRefs = mq.getTableReferences(rightInput);
-    if (rightTableRefs == null) {
-      // We could not infer the table refs from right input
-      return null;
-    }
-    for (RelTableRef rightRef : rightTableRefs) {
-      int shift = 0;
-      Collection<RelTableRef> lRefs =
-          leftQualifiedNamesToRefs.get(rightRef.getQualifiedName());
-      if (lRefs != null) {
-        shift = lRefs.size();
-      }
-      RelTableRef shiftTableRef =
-          RelTableRef.of(rightRef.getTable(), shift + 
rightRef.getEntityNumber());
-      assert !result.contains(shiftTableRef);
-      result.add(shiftTableRef);
-    }
+  /** Table references from RepeatUnion. */
+  public @Nullable Set<RelTableRef> getTableReferences(RepeatUnion rel, 
RelMetadataQuery mq) {
+    return getTableReferences(rel.getInputs(), ImmutableList.of(), mq);
+  }
 
-    // Return result
-    return result;
+  /** Table references from Combine. */
+  public @Nullable Set<RelTableRef> getTableReferences(Combine rel, 
RelMetadataQuery mq) {
+    return getTableReferences(rel.getInputs(), ImmutableList.of(), mq);
   }
 
   /**
@@ -160,31 +157,38 @@ public Set<RelTableRef> getTableReferences(TableScan rel, 
RelMetadataQuery mq) {
    * references.
    */
   public @Nullable Set<RelTableRef> getTableReferences(SetOp rel, 
RelMetadataQuery mq) {
-    return getTableReferences(rel.getInputs(), mq);
+    return getTableReferences(rel.getInputs(), ImmutableList.of(), mq);
   }
 
   /**
-   * Table references from the relational inputs of a TableFunctionScan.
+   * Table references from TableFunctionScan.
    *
-   * <p>Returns an empty set if there are no inputs, and {@code null} if the 
table
-   * references of any input cannot be determined. Tables accessed internally
-   * by the table function are not included.
+   * <p>Returns an empty set when its inputs and call reference no tables.
    */
   public @Nullable Set<RelTableRef> getTableReferences(TableFunctionScan rel,
       RelMetadataQuery mq) {
-    return getTableReferences(rel.getInputs(), mq);
+    return getTableReferences(rel.getInputs(), 
ImmutableList.of(rel.getCall()), mq);
   }
 
-  /** Returns the union of the table references of {@code inputs}, assigning
-   * distinct entity numbers to repeated references to the same table, or 
{@code null}
-   * if the references of any input cannot be determined. */
+  /** Returns the union of the table references of {@code inputs} and 
expression
+   * sub-queries, assigning distinct entity numbers to repeated references to
+   * the same table, or {@code null} if the references of any input or 
sub-query
+   * cannot be determined. */
   private static @Nullable Set<RelTableRef> getTableReferences(
-      List<RelNode> inputs, RelMetadataQuery mq) {
+      List<RelNode> inputs, List<? extends RexNode> expressions, 
RelMetadataQuery mq) {
+    final List<RelNode> rels = new ArrayList<>(inputs);
+    final RexUtil.SubQueryCollector collector = new 
RexUtil.SubQueryCollector(true);
+    for (RexNode expression : expressions) {
+      expression.accept(collector);
+    }
+    for (RexSubQuery subQuery : collector.getSubQueries()) {
+      rels.add(subQuery.rel);
+    }
     final Set<RelTableRef> result = new HashSet<>();
 
     // Infer column origin expressions for given references
     final Multimap<List<String>, RelTableRef> qualifiedNamesToRefs = 
HashMultimap.create();
-    for (RelNode input : inputs) {
+    for (RelNode input : rels) {
       final Map<RelTableRef, RelTableRef> currentTablesMapping = new 
HashMap<>();
       final Set<RelTableRef> inputTableRefs = mq.getTableReferences(input);
       if (inputTableRefs == null) {
@@ -218,35 +222,45 @@ public Set<RelTableRef> getTableReferences(TableScan rel, 
RelMetadataQuery mq) {
    * Table references from Project.
    */
   public @Nullable Set<RelTableRef> getTableReferences(Project rel, final 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getInput());
+    return getTableReferences(ImmutableList.of(rel.getInput()), 
rel.getProjects(), mq);
   }
 
   /**
    * Table references from Filter.
    */
   public @Nullable Set<RelTableRef> getTableReferences(Filter rel, 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getInput());
+    return getTableReferences(ImmutableList.of(rel.getInput()),
+        ImmutableList.of(rel.getCondition()), mq);
   }
 
   /**
    * Table references from Calc.
    */
   public @Nullable Set<RelTableRef> getTableReferences(Calc rel, 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getInput());
+    return getTableReferences(ImmutableList.of(rel.getInput()), 
rel.getProgram().getExprList(), mq);
   }
 
   /**
    * Table references from Sort.
    */
   public @Nullable Set<RelTableRef> getTableReferences(Sort rel, 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getInput());
+    final ImmutableList.Builder<RexNode> expressions = ImmutableList.builder();
+    if (rel.offset != null) {
+      expressions.add(rel.offset);
+    }
+    if (rel.fetch != null) {
+      expressions.add(rel.fetch);
+    }
+    return getTableReferences(ImmutableList.of(rel.getInput()), 
expressions.build(), mq);
   }
 
   /**
    * Table references from TableModify.
    */
   public @Nullable Set<RelTableRef> getTableReferences(TableModify rel, 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getInput());
+    final List<RexNode> expressions = rel.getSourceExpressionList();
+    return getTableReferences(ImmutableList.of(rel.getInput()),
+        expressions == null ? ImmutableList.of() : expressions, mq);
   }
 
   /**
@@ -260,7 +274,19 @@ public Set<RelTableRef> getTableReferences(TableScan rel, 
RelMetadataQuery mq) {
    * Table references from Window.
    */
   public @Nullable Set<RelTableRef> getTableReferences(Window rel, 
RelMetadataQuery mq) {
-    return mq.getTableReferences(rel.getInput());
+    final ImmutableList.Builder<RexNode> expressions = ImmutableList.builder();
+    for (Window.Group group : rel.groups) {
+      expressions.addAll(group.aggCalls);
+      final @Nullable RexNode lowerOffset = group.lowerBound.getOffset();
+      if (lowerOffset != null) {
+        expressions.add(lowerOffset);
+      }
+      final @Nullable RexNode upperOffset = group.upperBound.getOffset();
+      if (upperOffset != null) {
+        expressions.add(upperOffset);
+      }
+    }
+    return getTableReferences(ImmutableList.of(rel.getInput()), 
expressions.build(), mq);
   }
 
   /**
@@ -270,4 +296,38 @@ public Set<RelTableRef> getTableReferences(TableScan rel, 
RelMetadataQuery mq) {
     return mq.getTableReferences(rel.getInput());
   }
 
+  /** Table references from Snapshot. */
+  public @Nullable Set<RelTableRef> getTableReferences(Snapshot rel, 
RelMetadataQuery mq) {
+    return getTableReferences(ImmutableList.of(rel.getInput()),
+        ImmutableList.of(rel.getPeriod()), mq);
+  }
+
+  /** Table references from Match. */
+  public @Nullable Set<RelTableRef> getTableReferences(Match rel, 
RelMetadataQuery mq) {
+    final ImmutableList.Builder<RexNode> expressions = ImmutableList.builder();
+    expressions.add(rel.getPattern(), rel.getAfter());
+    expressions.addAll(rel.getMeasures().values());
+    expressions.addAll(rel.getPatternDefinitions().values());
+    final @Nullable RexNode interval = rel.getInterval();
+    if (interval != null) {
+      expressions.add(interval);
+    }
+    return getTableReferences(ImmutableList.of(rel.getInput()), 
expressions.build(), mq);
+  }
+
+  /** Table references from Uncollect. */
+  public @Nullable Set<RelTableRef> getTableReferences(Uncollect rel, 
RelMetadataQuery mq) {
+    return mq.getTableReferences(rel.getInput());
+  }
+
+  /** Table references from Collect. */
+  public @Nullable Set<RelTableRef> getTableReferences(Collect rel, 
RelMetadataQuery mq) {
+    return mq.getTableReferences(rel.getInput());
+  }
+
+  /** Table references from Spool. */
+  public @Nullable Set<RelTableRef> getTableReferences(Spool rel, 
RelMetadataQuery mq) {
+    return mq.getTableReferences(rel.getInput());
+  }
+
 }
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 13f89bad92..5c51eab9ce 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -3397,22 +3397,40 @@ public FixNullabilityShuttle(RexBuilder rexBuilder,
     }
   }
 
-  /** Visitor that collects all the top level SubQueries {@link RexSubQuery}
-   *  in a projection list of a given {@link Project}.*/
+  /** Visitor that collects {@link RexSubQuery} expressions. */
   public static class SubQueryCollector extends RexVisitorImpl<Void> {
     private final List<RexSubQuery> subQueries;
-    private SubQueryCollector() {
-      super(true);
+
+    /** Creates a collector.
+     *
+     * @param deep Whether to visit expression operands; relational expressions
+     *     inside sub-queries are never visited
+     */
+    public SubQueryCollector(boolean deep) {
+      super(deep);
       this.subQueries = new ArrayList<>();
     }
 
     @Override public Void visitSubQuery(RexSubQuery subQuery) {
       subQueries.add(subQuery);
-      return null;
+      return super.visitSubQuery(subQuery);
+    }
+
+    /** Returns an immutable snapshot of the sub-queries collected so far,
+     * in visitation order. */
+    public List<RexSubQuery> getSubQueries() {
+      return ImmutableList.copyOf(subQueries);
     }
 
-    public static List<RexSubQuery> collect(Project project) {
-      SubQueryCollector subQueryCollector = new SubQueryCollector();
+    /** Collects {@link RexSubQuery} expressions in the projection list
+     * of a given {@link Project}.
+     *
+     * @param project Project whose expressions are visited
+     * @param deep Whether to recursively visit expression operands; if false,
+     *     collects only top-level {@link RexSubQuery} expressions in the 
projection list
+     */
+    public static List<RexSubQuery> collect(Project project, boolean deep) {
+      SubQueryCollector subQueryCollector = new SubQueryCollector(deep);
       for (RexNode node : project.getProjects()) {
         node.accept(subQueryCollector);
       }
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 6123696154..76ad8bf50e 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -54,6 +54,7 @@
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.Sample;
 import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.Spool;
 import org.apache.calcite.rel.core.TableModify;
 import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Union;
@@ -64,8 +65,10 @@
 import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rel.logical.LogicalJoin;
 import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalRepeatUnion;
 import org.apache.calcite.rel.logical.LogicalSort;
 import org.apache.calcite.rel.logical.LogicalTableScan;
+import org.apache.calcite.rel.logical.LogicalTableSpool;
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.logical.LogicalValues;
 import org.apache.calcite.rel.metadata.BuiltInMetadata;
@@ -98,6 +101,7 @@
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexSubQuery;
 import org.apache.calcite.rex.RexTableInputRef;
 import org.apache.calcite.rex.RexTableInputRef.RelTableRef;
 import org.apache.calcite.rex.RexUtil;
@@ -4895,6 +4899,141 @@ public void checkAllPredicatesAndTableSetOp(String sql) 
{
     assertNull(tableReferences);
   }
 
+  @Test void testTableReferencesValues() {
+    final RelNode values = sql("select 5").toRel();
+    
assertThat(values.getCluster().getMetadataQuery().getTableReferences(values), 
empty());
+
+    final RelNode union = sql("select deptno from dept union all select 
5").toRel();
+    assertThat(union.getCluster().getMetadataQuery().getTableReferences(union),
+        sortsAs("[[CATALOG, SALES, DEPT].#0]"));
+  }
+
+  @Test void testTableReferencesSnapshot() {
+    final RelNode rel = sql("select productid from products_temporal "
+        + "for system_time as of TIMESTAMP '2011-01-02 00:00:00'").toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, PRODUCTS_TEMPORAL].#0]"));
+  }
+
+  @Test void testTableReferencesMatch() {
+    final RelNode rel = sql("select * from emp match_recognize ("
+        + " measures A.empno as x pattern (A) define A as A.empno > 0) as m")
+        .toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesUncollect() {
+    final RelNode rel = sql("select * from unnest(array[10, 20])").toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel), 
empty());
+  }
+
+  @Test void testTableReferencesCollect() {
+    final RelNode rel = sql("select array(select empno from emp)")
+        .withConfig(c -> c.withExpand(true)).toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesRepeatUnion() {
+    final RelNode rel = fixture().withRelFn(b -> {
+      final RelNode scan = b.scan("EMP").build();
+      return LogicalRepeatUnion.create(scan, scan, true, null);
+    }).toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[scott, EMP].#0, [scott, EMP].#1]"));
+  }
+
+  @Test void testTableReferencesTableSpool() {
+    final RelNode rel = fixture().withRelFn(b -> {
+      final TableScan scan = (TableScan) b.scan("EMP").build();
+      return LogicalTableSpool.create(scan, Spool.Type.LAZY, Spool.Type.LAZY,
+          scan.getTable());
+    }).toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[scott, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesCombine() {
+    final RelNode rel = fixture().withRelFn(b -> {
+      final RelNode scan = b.scan("EMP").build();
+      return b.combine(scan, scan).build();
+    }).toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[scott, EMP].#0, [scott, EMP].#1]"));
+  }
+
+  @Test void testTableReferencesProjectSubQuery() {
+    final RelNode rel = sql("select (select max(sal) from emp) from 
dept").toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesFilterSubQuery() {
+    final RelNode rel = sql("select * from dept where deptno in "
+        + "(select deptno from emp)").toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesJoinSubQuery() {
+    final RelNode rel = sql("select * from dept d join emp e "
+        + "on d.deptno = e.deptno and e.sal > (select avg(sal) from 
emp)").toRel();
+    assertThat(RexUtil.SubQueryFinder.containsSubQuery((Join) 
rel.getInput(0)), is(true));
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, EMP].#0, "
+            + "[CATALOG, SALES, EMP].#1]"));
+  }
+
+  @Test void testTableReferencesCalcSubQuery() {
+    final RelNode rel = sql("select (select max(sal) from emp) from 
dept").toRel();
+    final HepPlanner planner = new HepPlanner(new HepProgramBuilder()
+        .addRuleInstance(CoreRules.PROJECT_TO_CALC)
+        .build());
+    planner.setRoot(rel);
+    final RelNode calc = planner.findBestExp();
+    assertThat(calc.getCluster().getMetadataQuery().getTableReferences(calc),
+        sortsAs("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesCorrelate() {
+    final RelNode rel = sql("select * from dept d, lateral "
+        + "(select * from emp e where e.deptno = d.deptno)").toRel();
+    assertThat(rel.getInput(0), instanceOf(Correlate.class));
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesCorrelateSameTable() {
+    final RelNode rel = sql("select * from dept d, lateral "
+        + "(select * from dept e where e.deptno = d.deptno)").toRel();
+    assertThat(rel.getInput(0), instanceOf(Correlate.class));
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, DEPT].#0, [CATALOG, SALES, DEPT].#1]"));
+  }
+
+  @Test void testTableReferencesRepeatedTableInSubQuery() {
+    final RelNode rel = sql("select (select max(e2.sal) from emp e2) "
+        + "from emp e1").toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, EMP].#0, [CATALOG, SALES, EMP].#1]"));
+  }
+
+  @Test void testTableReferencesNestedSubQueryWithUnknownNode() {
+    final RelNode rel = fixture().withRelFn(b -> {
+      final RelNode left = b.scan("DEPT").build();
+      final RelNode right = b.scan("EMP").project(b.field("SAL")).build();
+      final RelNode unknown =
+          new DummyRelNode(right.getCluster(), right.getTraitSet(), right);
+      return b.push(left)
+          .project(
+              b.call(SqlStdOperatorTable.PLUS,
+              RexSubQuery.scalar(unknown), b.literal(1)))
+          .build();
+    }).toRel();
+    assertNull(rel.getCluster().getMetadataQuery().getTableReferences(rel));
+  }
+
   @Test void testAllPredicatesUnionMultiTable() {
     final String sql = "select x.sal from\n"
         + "(select a.deptno, a.sal from (select * from emp) as a\n"
@@ -4962,6 +5101,20 @@ public void checkAllPredicatesAndTableSetOp(String sql) {
     assertThat(mq.getTableReferences(rel), empty());
   }
 
+  @Test void testTableReferencesTableFunctionScanSubQuery() {
+    final RelNode rel = sql("select * from table(ramp((select count(*) from 
emp)))")
+        .toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, EMP].#0]"));
+  }
+
+  @Test void testTableReferencesTableFunctionScanNestedSubQuery() {
+    final RelNode rel = sql("select * from table(ramp(1 + "
+        + "(select count(*) from emp)))").toRel();
+    assertThat(rel.getCluster().getMetadataQuery().getTableReferences(rel),
+        sortsAs("[[CATALOG, SALES, EMP].#0]"));
+  }
+
   @Test void testTableReferencesTableFunctionScanUnknownInput() {
     final RelNode rel = fixture()
         .withRelFn(builder -> {
diff --git 
a/core/src/test/resources/org/apache/calcite/rel/metadata/janino/GeneratedMetadata_TableReferencesHandler.java
 
b/core/src/test/resources/org/apache/calcite/rel/metadata/janino/GeneratedMetadata_TableReferencesHandler.java
index 85e36d9c7d..c8cd0e39ce 100644
--- 
a/core/src/test/resources/org/apache/calcite/rel/metadata/janino/GeneratedMetadata_TableReferencesHandler.java
+++ 
b/core/src/test/resources/org/apache/calcite/rel/metadata/janino/GeneratedMetadata_TableReferencesHandler.java
@@ -66,26 +66,44 @@ private java.util.Set getTableReferences_(
       return 
provider0.getTableReferences((org.apache.calcite.rel.core.Aggregate) r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Calc) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.Calc) 
r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Collect) {
+      return 
provider0.getTableReferences((org.apache.calcite.rel.core.Collect) r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Combine) {
+      return 
provider0.getTableReferences((org.apache.calcite.rel.core.Combine) r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Correlate) {
+      return 
provider0.getTableReferences((org.apache.calcite.rel.core.Correlate) r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Exchange) {
       return 
provider0.getTableReferences((org.apache.calcite.rel.core.Exchange) r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Filter) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.Filter) 
r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Join) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.Join) 
r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Match) {
+      return provider0.getTableReferences((org.apache.calcite.rel.core.Match) 
r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Project) {
       return 
provider0.getTableReferences((org.apache.calcite.rel.core.Project) r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.RepeatUnion) {
+      return 
provider0.getTableReferences((org.apache.calcite.rel.core.RepeatUnion) r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Sample) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.Sample) 
r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.SetOp) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.SetOp) 
r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Snapshot) {
+      return 
provider0.getTableReferences((org.apache.calcite.rel.core.Snapshot) r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Sort) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.Sort) 
r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Spool) {
+      return provider0.getTableReferences((org.apache.calcite.rel.core.Spool) 
r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.TableFunctionScan) {
       return 
provider0.getTableReferences((org.apache.calcite.rel.core.TableFunctionScan) r, 
mq);
     } else if (r instanceof org.apache.calcite.rel.core.TableModify) {
       return 
provider0.getTableReferences((org.apache.calcite.rel.core.TableModify) r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.TableScan) {
       return 
provider0.getTableReferences((org.apache.calcite.rel.core.TableScan) r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Uncollect) {
+      return 
provider0.getTableReferences((org.apache.calcite.rel.core.Uncollect) r, mq);
+    } else if (r instanceof org.apache.calcite.rel.core.Values) {
+      return provider0.getTableReferences((org.apache.calcite.rel.core.Values) 
r, mq);
     } else if (r instanceof org.apache.calcite.rel.core.Window) {
       return provider0.getTableReferences((org.apache.calcite.rel.core.Window) 
r, mq);
     } else if (r instanceof org.apache.calcite.rel.RelNode) {

Reply via email to