You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ru...@apache.org on 2019/07/25 07:04:27 UTC

[calcite] branch master updated: [CALCITE-3109] Improvements on algebraic operators to express recursive queries (RepeatUnion & TableSpool)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new c1c4e31  [CALCITE-3109] Improvements on algebraic operators to express recursive queries (RepeatUnion & TableSpool)
c1c4e31 is described below

commit c1c4e31c849c4c9cb73056be95f5694ccd5fc2db
Author: rubenada <ru...@gmail.com>
AuthorDate: Thu Jun 13 12:18:38 2019 +0200

    [CALCITE-3109] Improvements on algebraic operators to express recursive queries (RepeatUnion & TableSpool)
---
 .../adapter/enumerable/EnumerableRepeatUnion.java  | 10 ++--
 .../enumerable/EnumerableRepeatUnionRule.java      |  2 +-
 .../adapter/enumerable/EnumerableTableSpool.java   | 12 +++--
 .../enumerable/EnumerableTableSpoolRule.java       |  2 +-
 .../org/apache/calcite/rel/core/RelFactories.java  | 12 ++---
 .../org/apache/calcite/rel/core/RepeatUnion.java   | 21 ++++-----
 .../org/apache/calcite/rel/core/TableSpool.java    | 13 +++---
 .../calcite/rel/logical/LogicalRepeatUnion.java    | 10 ++--
 .../calcite/rel/logical/LogicalTableSpool.java     | 12 ++---
 .../java/org/apache/calcite/tools/RelBuilder.java  | 54 +++++++++++++++++-----
 .../org/apache/calcite/test/RelBuilderTest.java    |  8 ++--
 .../EnumerableRepeatUnionHierarchyTest.java        |  8 ++--
 .../apache/calcite/linq4j/EnumerableDefaults.java  | 15 +++---
 13 files changed, 105 insertions(+), 74 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnion.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnion.java
index c845070..33b234a 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnion.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnion.java
@@ -42,14 +42,14 @@ public class EnumerableRepeatUnion extends RepeatUnion implements EnumerableRel
    * Creates an EnumerableRepeatUnion.
    */
   EnumerableRepeatUnion(RelOptCluster cluster, RelTraitSet traitSet,
-      RelNode seed, RelNode iterative, boolean all, int maxRep) {
-    super(cluster, traitSet, seed, iterative, all, maxRep);
+      RelNode seed, RelNode iterative, boolean all, int iterationLimit) {
+    super(cluster, traitSet, seed, iterative, all, iterationLimit);
   }
 
   @Override public EnumerableRepeatUnion copy(RelTraitSet traitSet, List<RelNode> inputs) {
     assert inputs.size() == 2;
     return new EnumerableRepeatUnion(getCluster(), traitSet,
-        inputs.get(0), inputs.get(1), all, maxRep);
+        inputs.get(0), inputs.get(1), all, iterationLimit);
   }
 
   @Override public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
@@ -58,7 +58,7 @@ public class EnumerableRepeatUnion extends RepeatUnion implements EnumerableRel
           "Only EnumerableRepeatUnion ALL is supported");
     }
 
-    // return repeatUnionAll(<seedExp>, <iterativeExp>, maxRep);
+    // return repeatUnionAll(<seedExp>, <iterativeExp>, iterationLimit);
 
     BlockBuilder builder = new BlockBuilder();
     RelNode seed = getSeedRel();
@@ -74,7 +74,7 @@ public class EnumerableRepeatUnion extends RepeatUnion implements EnumerableRel
         BuiltInMethod.REPEAT_UNION_ALL.method,
         seedExp,
         iterativeExp,
-        Expressions.constant(maxRep, int.class));
+        Expressions.constant(iterationLimit, int.class));
     builder.add(unionExp);
 
     PhysType physType = PhysTypeImpl.of(
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnionRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnionRule.java
index b7e78d4..b57118a 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnionRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRepeatUnionRule.java
@@ -49,7 +49,7 @@ public class EnumerableRepeatUnionRule extends ConverterRule {
         convert(seedRel, seedRel.getTraitSet().replace(out)),
         convert(iterativeRel, iterativeRel.getTraitSet().replace(out)),
         union.all,
-        union.maxRep);
+        union.iterationLimit);
   }
 }
 
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpool.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpool.java
index 6e726ec..2712453 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpool.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpool.java
@@ -21,6 +21,7 @@ import org.apache.calcite.linq4j.tree.BlockBuilder;
 import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelDistributionTraitDef;
@@ -44,13 +45,13 @@ import org.apache.calcite.util.BuiltInMethod;
 public class EnumerableTableSpool extends TableSpool implements EnumerableRel {
 
   private EnumerableTableSpool(RelOptCluster cluster, RelTraitSet traitSet,
-      RelNode input, Type readType, Type writeType, String tableName) {
-    super(cluster, traitSet, input, readType, writeType, tableName);
+      RelNode input, Type readType, Type writeType, RelOptTable table) {
+    super(cluster, traitSet, input, readType, writeType, table);
   }
 
   /** Creates an EnumerableTableSpool. */
   public static EnumerableTableSpool create(RelNode input, Type readType,
-      Type writeType, String tableName) {
+      Type writeType, RelOptTable table) {
     RelOptCluster cluster = input.getCluster();
     RelMetadataQuery mq = cluster.getMetadataQuery();
     RelTraitSet traitSet = cluster.traitSetOf(EnumerableConvention.INSTANCE)
@@ -58,7 +59,7 @@ public class EnumerableTableSpool extends TableSpool implements EnumerableRel {
             () -> mq.collations(input))
         .replaceIf(RelDistributionTraitDef.INSTANCE,
             () -> mq.distribution(input));
-    return new EnumerableTableSpool(cluster, traitSet, input, readType, writeType, tableName);
+    return new EnumerableTableSpool(cluster, traitSet, input, readType, writeType, table);
   }
 
   @Override public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
@@ -76,6 +77,7 @@ public class EnumerableTableSpool extends TableSpool implements EnumerableRel {
     RelNode input = getInput();
     Result inputResult = implementor.visitChild(this, 0, (EnumerableRel) input, pref);
 
+    String tableName = table.getQualifiedName().get(table.getQualifiedName().size() - 1);
     Expression tableExp = Expressions.convert_(
         Expressions.call(
             Expressions.call(
@@ -106,7 +108,7 @@ public class EnumerableTableSpool extends TableSpool implements EnumerableRel {
   @Override protected Spool copy(RelTraitSet traitSet, RelNode input,
       Type readType, Type writeType) {
     return new EnumerableTableSpool(input.getCluster(), traitSet, input,
-        readType, writeType, tableName);
+        readType, writeType, table);
   }
 }
 
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpoolRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpoolRule.java
index 27def0a..e5e6daf 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpoolRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableSpoolRule.java
@@ -46,7 +46,7 @@ public class EnumerableTableSpoolRule extends ConverterRule {
             spool.getInput().getTraitSet().replace(EnumerableConvention.INSTANCE)),
         spool.readType,
         spool.writeType,
-        spool.getTableName());
+        spool.getTable());
   }
 }
 
diff --git a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
index ff8af92..55127ff 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/RelFactories.java
@@ -619,7 +619,7 @@ public class RelFactories {
   public interface SpoolFactory {
     /** Creates a {@link TableSpool}. */
     RelNode createTableSpool(RelNode input, Spool.Type readType,
-        Spool.Type writeType, String tableName);
+        Spool.Type writeType, RelOptTable table);
   }
 
   /**
@@ -628,8 +628,8 @@ public class RelFactories {
    */
   private static class SpoolFactoryImpl implements SpoolFactory {
     public RelNode createTableSpool(RelNode input, Spool.Type readType,
-        Spool.Type writeType, String tableName) {
-      return LogicalTableSpool.create(input, readType, writeType, tableName);
+        Spool.Type writeType, RelOptTable table) {
+      return LogicalTableSpool.create(input, readType, writeType, table);
     }
   }
 
@@ -641,7 +641,7 @@ public class RelFactories {
   public interface RepeatUnionFactory {
     /** Creates a {@link RepeatUnion}. */
     RelNode createRepeatUnion(RelNode seed, RelNode iterative, boolean all,
-        int maxRep);
+        int iterationLimit);
   }
 
   /**
@@ -650,8 +650,8 @@ public class RelFactories {
    */
   private static class RepeatUnionFactoryImpl implements RepeatUnionFactory {
     public RelNode createRepeatUnion(RelNode seed, RelNode iterative,
-        boolean all, int maxRep) {
-      return LogicalRepeatUnion.create(seed, iterative, all, maxRep);
+        boolean all, int iterationLimit) {
+      return LogicalRepeatUnion.create(seed, iterative, all, iterationLimit);
     }
   }
 }
diff --git a/core/src/main/java/org/apache/calcite/rel/core/RepeatUnion.java b/core/src/main/java/org/apache/calcite/rel/core/RepeatUnion.java
index 8d8eb52..8d76298 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/RepeatUnion.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/RepeatUnion.java
@@ -58,36 +58,33 @@ public abstract class RepeatUnion extends BiRel {
   public final boolean all;
 
   /**
-   * Maximum number of times to repeat the iterative relational expression; -1
-   * means no limit, 0 means only seed will be evaluated
+   * Maximum number of times to repeat the iterative relational expression;
+   * negative value means no limit, 0 means only seed will be evaluated
    */
-  public final int maxRep;
+  public final int iterationLimit;
 
   //~ Constructors -----------------------------------------------------------
   protected RepeatUnion(RelOptCluster cluster, RelTraitSet traitSet,
-      RelNode seed, RelNode iterative, boolean all, int maxRep) {
+      RelNode seed, RelNode iterative, boolean all, int iterationLimit) {
     super(cluster, traitSet, seed, iterative);
-    if (maxRep < -1) {
-      throw new IllegalArgumentException("Wrong maxRep value");
-    }
-    this.maxRep = maxRep;
+    this.iterationLimit = iterationLimit;
     this.all = all;
   }
 
   @Override public double estimateRowCount(RelMetadataQuery mq) {
     // TODO implement a more accurate row count?
     double seedRowCount = mq.getRowCount(getSeedRel());
-    if (maxRep == 0) {
+    if (iterationLimit == 0) {
       return seedRowCount;
     }
     return seedRowCount
-        + mq.getRowCount(getIterativeRel()) * (maxRep != -1 ? maxRep : 10);
+        + mq.getRowCount(getIterativeRel()) * (iterationLimit < 0 ? 10 : iterationLimit);
   }
 
   @Override public RelWriter explainTerms(RelWriter pw) {
     super.explainTerms(pw);
-    if (maxRep != -1) {
-      pw.item("maxRep", maxRep);
+    if (iterationLimit >= 0) {
+      pw.item("iterationLimit", iterationLimit);
     }
     return pw.item("all", all);
   }
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableSpool.java b/core/src/main/java/org/apache/calcite/rel/core/TableSpool.java
index d7d8bc7..22506fb 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableSpool.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableSpool.java
@@ -18,6 +18,7 @@ package org.apache.calcite.rel.core;
 
 import org.apache.calcite.linq4j.function.Experimental;
 import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
@@ -33,21 +34,21 @@ import java.util.Objects;
 @Experimental
 public abstract class TableSpool extends Spool {
 
-  protected final String tableName;
+  protected final RelOptTable table;
 
   protected TableSpool(RelOptCluster cluster, RelTraitSet traitSet,
-      RelNode input, Type readType, Type writeType, String tableName) {
+      RelNode input, Type readType, Type writeType, RelOptTable table) {
     super(cluster, traitSet, input, readType, writeType);
-    this.tableName = Objects.requireNonNull(tableName);
+    this.table = Objects.requireNonNull(table);
   }
 
-  public String getTableName() {
-    return tableName;
+  public RelOptTable getTable() {
+    return table;
   }
 
   @Override public RelWriter explainTerms(RelWriter pw) {
     super.explainTerms(pw);
-    return pw.item("tableName", tableName);
+    return pw.item("table", table.getQualifiedName());
   }
 }
 
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalRepeatUnion.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalRepeatUnion.java
index c0c0a32..0a74382 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalRepeatUnion.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalRepeatUnion.java
@@ -37,8 +37,8 @@ public class LogicalRepeatUnion extends RepeatUnion {
 
   //~ Constructors -----------------------------------------------------------
   private LogicalRepeatUnion(RelOptCluster cluster, RelTraitSet traitSet,
-      RelNode seed, RelNode iterative, boolean all, int maxRep) {
-    super(cluster, traitSet, seed, iterative, all, maxRep);
+      RelNode seed, RelNode iterative, boolean all, int iterationLimit) {
+    super(cluster, traitSet, seed, iterative, all, iterationLimit);
   }
 
   /** Creates a LogicalRepeatUnion. */
@@ -49,10 +49,10 @@ public class LogicalRepeatUnion extends RepeatUnion {
 
   /** Creates a LogicalRepeatUnion. */
   public static LogicalRepeatUnion create(RelNode seed, RelNode iterative,
-      boolean all, int maxRep) {
+      boolean all, int iterationLimit) {
     RelOptCluster cluster = seed.getCluster();
     RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE);
-    return new LogicalRepeatUnion(cluster, traitSet, seed, iterative, all, maxRep);
+    return new LogicalRepeatUnion(cluster, traitSet, seed, iterative, all, iterationLimit);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -62,7 +62,7 @@ public class LogicalRepeatUnion extends RepeatUnion {
     assert traitSet.containsIfApplicable(Convention.NONE);
     assert inputs.size() == 2;
     return new LogicalRepeatUnion(getCluster(), traitSet,
-        inputs.get(0), inputs.get(1), all, maxRep);
+        inputs.get(0), inputs.get(1), all, iterationLimit);
   }
 }
 
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableSpool.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableSpool.java
index 8b02985..c7dc798 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableSpool.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableSpool.java
@@ -19,6 +19,7 @@ package org.apache.calcite.rel.logical;
 import org.apache.calcite.linq4j.function.Experimental;
 import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelDistributionTraitDef;
@@ -39,13 +40,13 @@ public class LogicalTableSpool extends TableSpool {
 
   //~ Constructors -----------------------------------------------------------
   public LogicalTableSpool(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
-      Type readType, Type writeType, String tableName) {
-    super(cluster, traitSet, input, readType, writeType, tableName);
+      Type readType, Type writeType, RelOptTable table) {
+    super(cluster, traitSet, input, readType, writeType, table);
   }
 
   /** Creates a LogicalTableSpool. */
   public static LogicalTableSpool create(RelNode input, Type readType,
-      Type writeType, String tableName) {
+      Type writeType, RelOptTable table) {
     RelOptCluster cluster = input.getCluster();
     RelMetadataQuery mq = cluster.getMetadataQuery();
     RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE)
@@ -53,8 +54,7 @@ public class LogicalTableSpool extends TableSpool {
             () -> mq.collations(input))
         .replaceIf(RelDistributionTraitDef.INSTANCE,
             () -> mq.distribution(input));
-    return new LogicalTableSpool(cluster, traitSet, input, readType, writeType,
-        tableName);
+    return new LogicalTableSpool(cluster, traitSet, input, readType, writeType, table);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -62,7 +62,7 @@ public class LogicalTableSpool extends TableSpool {
   @Override protected Spool copy(RelTraitSet traitSet, RelNode input,
       Type readType, Type writeType) {
     return new LogicalTableSpool(input.getCluster(), traitSet, input,
-        readType, writeType, tableName);
+        readType, writeType, table);
   }
 }
 
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index f19a510..da28f3f 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -30,6 +30,7 @@ import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.RelHomogeneousShuttle;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
@@ -1806,7 +1807,7 @@ public class RelBuilder {
    */
   @Experimental
   public RelBuilder transientScan(String tableName, RelDataType rowType) {
-    ListTransientTable transientTable = new ListTransientTable(tableName, rowType);
+    TransientTable transientTable = new ListTransientTable(tableName, rowType);
     RelOptTable relOptTable = RelOptTableImpl.create(
         relOptSchema,
         rowType,
@@ -1823,11 +1824,10 @@ public class RelBuilder {
    *
    * @param readType Spool's read type (as described in {@link Spool.Type})
    * @param writeType Spool's write type (as described in {@link Spool.Type})
-   * @param tableName Table name
+   * @param table Table to write into
    */
-  private RelBuilder tableSpool(Spool.Type readType, Spool.Type writeType,
-      String tableName) {
-    RelNode spool =  spoolFactory.createTableSpool(peek(), readType, writeType, tableName);
+  private RelBuilder tableSpool(Spool.Type readType, Spool.Type writeType, RelOptTable table) {
+    RelNode spool =  spoolFactory.createTableSpool(peek(), readType, writeType, table);
     replaceTop(spool);
     return this;
   }
@@ -1866,14 +1866,46 @@ public class RelBuilder {
    * @param tableName Name of the {@link TransientTable} associated to the
    *     {@link RepeatUnion}
    * @param all Whether duplicates are considered
-   * @param maxRep Maximum number of iterations; -1 means no limit
+   * @param iterationLimit Maximum number of iterations; negative value means no limit
    */
   @Experimental
-  public RelBuilder repeatUnion(String tableName, boolean all, int maxRep) {
-    RelNode iterative = tableSpool(Spool.Type.LAZY, Spool.Type.LAZY, tableName).build();
-    RelNode seed = tableSpool(Spool.Type.LAZY, Spool.Type.LAZY, tableName).build();
-    RelNode repeatUnion = repeatUnionFactory.createRepeatUnion(seed, iterative, all, maxRep);
-    return push(repeatUnion);
+  public RelBuilder repeatUnion(String tableName, boolean all, int iterationLimit) {
+    RelOptTableFinder finder = new RelOptTableFinder(tableName);
+    for (int i = 0; i < stack.size(); i++) { // search scan(tableName) in the stack
+      peek(i).accept(finder);
+      if (finder.relOptTable != null) { // found
+        break;
+      }
+    }
+    if (finder.relOptTable == null) {
+      throw RESOURCE.tableNotFound(tableName).ex();
+    }
+
+    RelNode iterative = tableSpool(Spool.Type.LAZY, Spool.Type.LAZY, finder.relOptTable).build();
+    RelNode seed = tableSpool(Spool.Type.LAZY, Spool.Type.LAZY, finder.relOptTable).build();
+    RelNode repUnion = repeatUnionFactory.createRepeatUnion(seed, iterative, all, iterationLimit);
+    return push(repUnion);
+  }
+
+  /**
+   * Auxiliary class to find a certain RelOptTable based on its name
+   */
+  private static final class RelOptTableFinder extends RelHomogeneousShuttle {
+    private RelOptTable relOptTable = null;
+    private final String tableName;
+
+    private RelOptTableFinder(String tableName) {
+      this.tableName = tableName;
+    }
+
+    @Override public RelNode visit(TableScan scan) {
+      final RelOptTable scanTable = scan.getTable();
+      final List<String> qualifiedName = scanTable.getQualifiedName();
+      if (qualifiedName.get(qualifiedName.size() - 1).equals(tableName)) {
+        relOptTable = scanTable;
+      }
+      return super.visit(scan);
+    }
   }
 
   /** Creates a {@link Join}. */
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 1f8f844..b7e38cc 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -1403,9 +1403,9 @@ public class RelBuilderTest {
             .repeatUnion("DELTA_TABLE", true)
             .build();
     final String expected = "LogicalRepeatUnion(all=[true])\n"
-        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], tableName=[DELTA_TABLE])\n"
+        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], table=[[DELTA_TABLE]])\n"
         + "    LogicalValues(tuples=[[{ 1 }]])\n"
-        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], tableName=[DELTA_TABLE])\n"
+        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], table=[[DELTA_TABLE]])\n"
         + "    LogicalProject($f0=[+($0, 1)])\n"
         + "      LogicalFilter(condition=[<($0, 10)])\n"
         + "        LogicalTableScan(table=[[DELTA_TABLE]])\n";
@@ -1443,9 +1443,9 @@ public class RelBuilderTest {
             .repeatUnion("AUX", true)
             .build();
     final String expected = "LogicalRepeatUnion(all=[true])\n"
-        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], tableName=[AUX])\n"
+        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], table=[[AUX]])\n"
         + "    LogicalValues(tuples=[[{ 0, 1 }]])\n"
-        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], tableName=[AUX])\n"
+        + "  LogicalTableSpool(readType=[LAZY], writeType=[LAZY], table=[[AUX]])\n"
         + "    LogicalProject(n=[+($0, 1)], fact=[*(+($0, 1), $1)])\n"
         + "      LogicalFilter(condition=[<($0, 7)])\n"
         + "        LogicalTableScan(table=[[AUX]])\n";
diff --git a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableRepeatUnionHierarchyTest.java b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableRepeatUnionHierarchyTest.java
index 50733e1..955a149 100644
--- a/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableRepeatUnionHierarchyTest.java
+++ b/core/src/test/java/org/apache/calcite/test/enumerable/EnumerableRepeatUnionHierarchyTest.java
@@ -61,9 +61,9 @@ public class EnumerableRepeatUnionHierarchyTest {
   public static Iterable<Object[]> data() {
     return Arrays.asList(new Object[][] {
         { 1, true, -1, new String[]{EMP1} },
-        { 2, true, -1, new String[]{EMP2, EMP1} },
+        { 2, true, -2, new String[]{EMP2, EMP1} },
         { 3, true, -1, new String[]{EMP3, EMP2, EMP1} },
-        { 4, true, -1, new String[]{EMP4, EMP1} },
+        { 4, true, -5, new String[]{EMP4, EMP1} },
         { 5, true, -1, new String[]{EMP5, EMP2, EMP1} },
         { 3, true,  0, new String[]{EMP3} },
         { 3, true,  1, new String[]{EMP3, EMP2} },
@@ -71,8 +71,8 @@ public class EnumerableRepeatUnionHierarchyTest {
         { 3, true, 10, new String[]{EMP3, EMP2, EMP1} },
 
         { 1, false, -1, new String[]{EMP1, EMP2, EMP4, EMP3, EMP5} },
-        { 2, false, -1, new String[]{EMP2, EMP3, EMP5} },
-        { 3, false, -1, new String[]{EMP3} },
+        { 2, false, -10, new String[]{EMP2, EMP3, EMP5} },
+        { 3, false, -100, new String[]{EMP3} },
         { 4, false, -1, new String[]{EMP4} },
         { 1, false,  0, new String[]{EMP1} },
         { 1, false,  1, new String[]{EMP1, EMP2, EMP4} },
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
index 2f2683c..459a564 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/EnumerableDefaults.java
@@ -3379,21 +3379,21 @@ public abstract class EnumerableDefaults {
    * no results, or an optional maximum numbers of iterations is reached
    * @param seed seed enumerable
    * @param iteration iteration enumerable
-   * @param maxRep maximum numbers of repetitions for the iteration enumerable (-1 means no limit)
+   * @param iterationLimit maximum numbers of repetitions for the iteration enumerable
+   *                       (negative value means no limit)
    * @param <TSource> record type
    */
   @SuppressWarnings("unchecked")
   public static <TSource> Enumerable<TSource> repeatUnionAll(
           Enumerable<TSource> seed,
           Enumerable<TSource> iteration,
-          int maxRep) {
-    assert maxRep >= -1;
+          int iterationLimit) {
     return new AbstractEnumerable<TSource>() {
       @Override public Enumerator<TSource> enumerator() {
         return new Enumerator<TSource>() {
           private TSource current = (TSource) DUMMY;
           private boolean seedProcessed = false;
-          private int currentRep = 0;
+          private int currentIteration = 0;
           private final Enumerator<TSource> seedEnumerator = seed.enumerator();
           private Enumerator<TSource> iterativeEnumerator = null;
 
@@ -3417,7 +3417,7 @@ public abstract class EnumerableDefaults {
 
             // if we are done with the seed, moveNext on the iterative part
             while (true) {
-              if (maxRep != -1 && this.currentRep == maxRep) {
+              if (iterationLimit >= 0 && this.currentIteration == iterationLimit) {
                 // max number of iterations reached, we are done
                 this.current = (TSource) DUMMY;
                 return false;
@@ -3441,7 +3441,7 @@ public abstract class EnumerableDefaults {
               this.current = (TSource) DUMMY;
               this.iterativeEnumerator.close();
               this.iterativeEnumerator = null;
-              this.currentRep++;
+              this.currentIteration++;
             }
           }
 
@@ -3451,14 +3451,13 @@ public abstract class EnumerableDefaults {
               this.iterativeEnumerator.close();
               this.iterativeEnumerator = null;
             }
-            this.currentRep = 0;
+            this.currentIteration = 0;
           }
 
           @Override public void close() {
             this.seedEnumerator.close();
             if (this.iterativeEnumerator != null) {
               this.iterativeEnumerator.close();
-              this.iterativeEnumerator = null;
             }
           }
         };