This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 65492bc162c [fix](rewrite rule) Reject mismatched compare plan in
PullUpJoinFromUnionAll (#65472)
65492bc162c is described below
commit 65492bc162cfe8c0d73672a48e4db1ac5f919ae5
Author: yujun <[email protected]>
AuthorDate: Fri Jul 24 14:36:57 2026 +0800
[fix](rewrite rule) Reject mismatched compare plan in
PullUpJoinFromUnionAll (#65472)
Avoid comparing and remapping join inputs as equivalent when the
common-side projects expose different output arity. This keeps the union
pull-up rewrite aligned with the actual plan shape and adds focused
coverage for the comparator and rewrite guard.
Key changes:
- Add an output-size guard before comparing plan nodes in
PullUpJoinFromUnionAll.
- Add a FE unit test that covers both the comparator and the rewrite
path.
Unit Test:
- PullUpJoinFromUnionAllTest
relate PR: #28682
---
.../catalog/stream/OlapTableStreamWrapper.java | 4 +
.../rules/rewrite/PullUpJoinFromUnionAll.java | 11 +-
.../plans/logical/LogicalCatalogRelation.java | 42 ++
.../trees/plans/logical/LogicalFileScan.java | 29 ++
.../trees/plans/logical/LogicalHudiScan.java | 10 +
.../trees/plans/logical/LogicalOdbcScan.java | 5 +
.../trees/plans/logical/LogicalOlapScan.java | 25 +
.../plans/logical/LogicalOlapTableStreamScan.java | 25 +
.../trees/plans/logical/LogicalSchemaScan.java | 13 +
.../trees/plans/logical/LogicalTestScan.java | 5 +
.../java/org/apache/doris/nereids/util/Utils.java | 7 +
.../rules/rewrite/PullUpJoinFromUnionAllTest.java | 509 +++++++++++++++++++++
12 files changed, 679 insertions(+), 6 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamWrapper.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamWrapper.java
index 92c9701884a..b6f5bcd0f8b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamWrapper.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/catalog/stream/OlapTableStreamWrapper.java
@@ -213,6 +213,10 @@ public class OlapTableStreamWrapper extends OlapTable {
return baseTable;
}
+ public KeysType getStreamKeysType() {
+ return keysType;
+ }
+
public BaseTableStream.StreamScanType getStreamScanType() {
if (keysType == KeysType.DUP_KEYS) {
return BaseTableStream.StreamScanType.APPEND_ONLY;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAll.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAll.java
index f7197516011..803749366bd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAll.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAll.java
@@ -17,7 +17,6 @@
package org.apache.doris.nereids.rules.rewrite;
-import org.apache.doris.catalog.constraint.TableIdentifier;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
@@ -577,14 +576,14 @@ public class PullUpJoinFromUnionAll extends
OneRewriteRuleFactory {
}
boolean comparePlan(Plan plan1, Plan plan2) {
+ if (plan1.getOutput().size() != plan2.getOutput().size()) {
+ return false;
+ }
boolean isEqual = true;
if (plan1 instanceof LogicalCatalogRelation && plan2 instanceof
LogicalCatalogRelation) {
- isEqual = new TableIdentifier(((LogicalCatalogRelation)
plan1).getTable())
- .equals(new TableIdentifier(((LogicalCatalogRelation)
plan2).getTable()));
+ isEqual = ((LogicalCatalogRelation) plan1)
+ .hasSameScanSemantics((LogicalCatalogRelation) plan2);
} else if (plan1 instanceof LogicalProject && plan2 instanceof
LogicalProject) {
- if (plan1.getOutput().size() != plan2.getOutput().size()) {
- isEqual = false;
- }
for (int i = 0; isEqual && i < plan2.getOutput().size(); i++) {
Expression expr1 = ((LogicalProject<?>)
plan1).getProjects().get(i);
Expression expr2 = ((LogicalProject<?>)
plan2).getProjects().get(i);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
index 1fc43325d18..61a6dd54fbd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
@@ -23,6 +23,7 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.constraint.ConstraintManager;
import org.apache.doris.catalog.constraint.PrimaryKeyConstraint;
+import org.apache.doris.catalog.constraint.TableIdentifier;
import org.apache.doris.catalog.constraint.UniqueConstraint;
import org.apache.doris.catalog.info.TableNameInfo;
import org.apache.doris.catalog.stream.StreamReadMode;
@@ -251,6 +252,47 @@ public abstract class LogicalCatalogRelation extends
LogicalRelation implements
return this;
}
+ /** Compare whether two catalog relations read the same data with the same
output semantics. */
+ public final boolean hasSameScanSemantics(LogicalCatalogRelation other) {
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ if (!hasSameTableIdentity(other)) {
+ return false;
+ }
+ if (getOutput().size() != other.getOutput().size()) {
+ return false;
+ }
+ for (int i = 0; i < getOutput().size(); i++) {
+ if (!hasSameOutputSlotSemantics(getOutput().get(i),
other.getOutput().get(i))) {
+ return false;
+ }
+ }
+ return hasSameScanState(other);
+ }
+
+ protected boolean hasSameTableIdentity(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ return new TableIdentifier(table).equals(new
TableIdentifier(other.table));
+ }
+
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ return false;
+ }
+
+ private boolean hasSameOutputSlotSemantics(Slot left, Slot right) {
+ if (!(left instanceof SlotReference) || !(right instanceof
SlotReference)) {
+ return false;
+ }
+ SlotReference leftSlot = (SlotReference) left;
+ SlotReference rightSlot = (SlotReference) right;
+ return Objects.equals(left.getClass(), right.getClass())
+ && Objects.equals(leftSlot.getName(), rightSlot.getName())
+ && Objects.equals(leftSlot.getSubPath(),
rightSlot.getSubPath());
+ }
+
public abstract LogicalCatalogRelation withRelationId(RelationId
relationId);
/**
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFileScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFileScan.java
index 29e2f84feac..341818e6da6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFileScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFileScan.java
@@ -198,6 +198,18 @@ public class LogicalFileScan extends
LogicalCatalogRelation implements SupportPr
return super.equals(o) && Objects.equals(selectedPartitions,
((LogicalFileScan) o).selectedPartitions);
}
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ LogicalFileScan that = (LogicalFileScan) other;
+ return Objects.equals(selectedPartitions, that.selectedPartitions)
+ && Objects.equals(tableSample, that.tableSample)
+ && hasSameSnapshot(tableSnapshot, that.tableSnapshot)
+ && hasSameScanParams(scanParams, that.scanParams);
+ }
+
@Override
public List<Slot> computeOutput() {
if (cachedOutputs.isPresent()) {
@@ -266,6 +278,23 @@ public class LogicalFileScan extends
LogicalCatalogRelation implements SupportPr
return false;
}
+ private boolean hasSameSnapshot(Optional<TableSnapshot> left,
Optional<TableSnapshot> right) {
+ if (!left.isPresent() || !right.isPresent()) {
+ return left.isPresent() == right.isPresent();
+ }
+ return left.get().getType() == right.get().getType()
+ && Objects.equals(left.get().getValue(),
right.get().getValue());
+ }
+
+ private boolean hasSameScanParams(Optional<TableScanParams> left,
Optional<TableScanParams> right) {
+ if (!left.isPresent() || !right.isPresent()) {
+ return left.isPresent() == right.isPresent();
+ }
+ return Objects.equals(left.get().getParamType(),
right.get().getParamType())
+ && Objects.equals(left.get().getMapParams(),
right.get().getMapParams())
+ && Objects.equals(left.get().getListParams(),
right.get().getListParams());
+ }
+
/**
* SelectedPartitions contains the selected partitions and the total
partition number.
* Mainly for hive table partition pruning.
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHudiScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHudiScan.java
index 5ff0f937444..97b2d94e27e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHudiScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHudiScan.java
@@ -121,6 +121,16 @@ public class LogicalHudiScan extends LogicalFileScan {
return incrementalRelation;
}
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ LogicalHudiScan that = (LogicalHudiScan) other;
+ // IncrementalRelation contains the resolved Hudi timeline and split
state and has no value equality.
+ return super.hasSameScanState(other) &&
Objects.equals(incrementalRelation, that.incrementalRelation);
+ }
+
/**
* replace incremental params as AND expression
* incr('beginTime'='20240308110257169', 'endTime'='20240308110677278') =>
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOdbcScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOdbcScan.java
index a4d66919283..e0b64133cef 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOdbcScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOdbcScan.java
@@ -95,6 +95,11 @@ public class LogicalOdbcScan extends LogicalCatalogRelation {
Optional.of(getLogicalProperties()), tableAlias));
}
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ return Utils.isSameClass(this, other);
+ }
+
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitLogicalOdbcScan(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
index 71fb7589c47..94bed913bcb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
@@ -1207,6 +1207,22 @@ public class LogicalOlapScan extends
LogicalCatalogRelation implements OlapScan,
Optional.of(scanParams)));
}
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ LogicalOlapScan that = (LogicalOlapScan) other;
+ return selectedIndexId == that.selectedIndexId
+ && indexSelected == that.indexSelected
+ && Objects.equals(selectedPartitionIds,
that.selectedPartitionIds)
+ && Objects.equals(manuallySpecifiedPartitions,
that.manuallySpecifiedPartitions)
+ && Objects.equals(selectedTabletIds, that.selectedTabletIds)
+ && Objects.equals(manuallySpecifiedTabletIds,
that.manuallySpecifiedTabletIds)
+ && Objects.equals(tableSample, that.tableSample)
+ && hasSameScanParams(scanParams, that.scanParams);
+ }
+
@Override
public boolean supportPruneNestedColumn() {
return true;
@@ -1215,4 +1231,13 @@ public class LogicalOlapScan extends
LogicalCatalogRelation implements OlapScan,
public Optional<TableScanParams> getScanParams() {
return scanParams;
}
+
+ private boolean hasSameScanParams(Optional<TableScanParams> left,
Optional<TableScanParams> right) {
+ if (!left.isPresent() || !right.isPresent()) {
+ return left.isPresent() == right.isPresent();
+ }
+ return Objects.equals(left.get().getParamType(),
right.get().getParamType())
+ && Objects.equals(left.get().getMapParams(),
right.get().getMapParams())
+ && Objects.equals(left.get().getListParams(),
right.get().getListParams());
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapTableStreamScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapTableStreamScan.java
index 73a4d0441b4..47ba78d7a61 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapTableStreamScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapTableStreamScan.java
@@ -21,6 +21,7 @@ import org.apache.doris.analysis.TableScanParams;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Table;
+import org.apache.doris.catalog.constraint.TableIdentifier;
import org.apache.doris.catalog.stream.OlapTableStreamWrapper;
import org.apache.doris.catalog.stream.StreamReadMode;
import org.apache.doris.common.IdGenerator;
@@ -464,6 +465,30 @@ public class LogicalOlapTableStreamScan extends
LogicalOlapScan {
scanParams, readMode));
}
+ @Override
+ protected boolean hasSameTableIdentity(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ LogicalOlapTableStreamScan that = (LogicalOlapTableStreamScan) other;
+ return Objects.equals(getTable().getStreamDbId(),
that.getTable().getStreamDbId())
+ && Objects.equals(getTable().getStreamId(),
that.getTable().getStreamId())
+ && new TableIdentifier(getTable().getBaseTable())
+ .equals(new TableIdentifier(that.getTable().getBaseTable()));
+ }
+
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ LogicalOlapTableStreamScan that = (LogicalOlapTableStreamScan) other;
+ return super.hasSameScanState(other)
+ && readMode == that.readMode
+ && getTable().getStreamKeysType() ==
that.getTable().getStreamKeysType()
+ && Objects.equals(getTable().getOutputUpdateMap(),
that.getTable().getOutputUpdateMap());
+ }
+
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitLogicalOlapTableStreamScan(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSchemaScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSchemaScan.java
index c441cfbb15b..f42ee2640fe 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSchemaScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSchemaScan.java
@@ -113,6 +113,19 @@ public class LogicalSchemaScan extends
LogicalCatalogRelation {
return frontendConjuncts;
}
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ if (!Utils.isSameClass(this, other)) {
+ return false;
+ }
+ LogicalSchemaScan that = (LogicalSchemaScan) other;
+ return filterPushed == that.filterPushed
+ && Objects.equals(schemaCatalog, that.schemaCatalog)
+ && Objects.equals(schemaDatabase, that.schemaDatabase)
+ && Objects.equals(schemaTable, that.schemaTable)
+ && Objects.equals(frontendConjuncts, that.frontendConjuncts);
+ }
+
@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitLogicalSchemaScan(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTestScan.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTestScan.java
index e49df118b9c..58bca515fdb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTestScan.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTestScan.java
@@ -85,6 +85,11 @@ public class LogicalTestScan extends LogicalCatalogRelation {
Optional.of(getLogicalProperties()), tableAlias));
}
+ @Override
+ protected boolean hasSameScanState(LogicalCatalogRelation other) {
+ return Utils.isSameClass(this, other);
+ }
+
@Override
public LogicalTestScan withRelationId(RelationId relationId) {
throw new RuntimeException("should not call LogicalTestScan's
withRelationId method");
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
index c268f01f73c..31d3f6fc29d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
@@ -62,6 +62,13 @@ import java.util.stream.Stream;
public class Utils {
public static final boolean enableAssert;
+ /**
+ * Check whether two objects are non-null and have the same concrete class.
+ */
+ public static boolean isSameClass(Object left, Object right) {
+ return left != null && right != null && left.getClass() ==
right.getClass();
+ }
+
static {
boolean enabled = false;
// if run jvm with -ea or -enableassertions, the assert statement will
be executed
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAllTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAllTest.java
new file mode 100644
index 00000000000..e10965911c9
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PullUpJoinFromUnionAllTest.java
@@ -0,0 +1,509 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.rewrite;
+
+import org.apache.doris.analysis.TableScanParams;
+import org.apache.doris.analysis.TableSnapshot;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.KeysType;
+import org.apache.doris.catalog.OdbcTable;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.Partition;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.catalog.stream.OlapTableStreamWrapper;
+import org.apache.doris.catalog.stream.StreamReadMode;
+import org.apache.doris.common.Pair;
+import org.apache.doris.datasource.ExternalTable;
+import org.apache.doris.datasource.hudi.source.IncrementalRelation;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.RelationId;
+import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFileScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalHudiScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOdbcScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapTableStreamScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSchemaScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalTestScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
+import org.apache.doris.nereids.util.MemoTestUtils;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.nereids.util.PlanConstructor;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+class PullUpJoinFromUnionAllTest {
+
+ @Test
+ void comparatorRejectsDifferentProjectOutputSizes() {
+ LogicalOlapScan smallScan = newScan(1, "common_small");
+ LogicalOlapScan largeScan = newScan(1, "common_large");
+ LogicalProject<LogicalOlapScan> smallProject =
project(selectSlots(smallScan.getOutput(), 0), smallScan);
+ LogicalProject<LogicalOlapScan> largeProject =
project(selectSlots(largeScan.getOutput(), 0, 1), largeScan);
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(largeProject,
smallProject));
+ }
+
+ @Test
+ void comparatorRejectsDifferentFilterChildOutputSizes() {
+ LogicalFilter<LogicalOlapScan> smallFilter =
filter(newCachedOutputScan(1, "common_filter", 0));
+ LogicalFilter<LogicalOlapScan> largeFilter =
filter(newCachedOutputScan(1, "common_filter", 0, 1));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(largeFilter,
smallFilter));
+ }
+
+ @Test
+ void comparatorRejectsDifferentMaterializedIndexSelections() {
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ LogicalOlapScan baseScan = newScanWithExtraIndex(11, "common_index",
101);
+ LogicalOlapScan indexScan =
baseScan.withMaterializedIndexSelected(101);
+
+ Assertions.assertFalse(comparator.isLogicalEqual(baseScan, indexScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentSelectedPartitions() {
+ OlapTable table = Mockito.mock(OlapTable.class);
+ Mockito.when(table.getId()).thenReturn(12L);
+ Mockito.when(table.getName()).thenReturn("common_partition");
+ Mockito.when(table.getDatabase()).thenReturn(null);
+ Mockito.when(table.getBaseIndexId()).thenReturn(1L);
+ Mockito.when(table.getBaseSchema()).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+
Mockito.when(table.getPartition(1L)).thenReturn(Mockito.mock(Partition.class));
+
Mockito.when(table.getPartition(2L)).thenReturn(Mockito.mock(Partition.class));
+
+ LogicalOlapScan firstPartitionScan = newPartitionedScan(table)
+ .withSelectedPartitionIds(ImmutableList.of(1L), true);
+ LogicalOlapScan secondPartitionScan = newPartitionedScan(table)
+ .withSelectedPartitionIds(ImmutableList.of(2L), true);
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(firstPartitionScan,
secondPartitionScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentHudiIncrementalRelations() {
+ ExternalTable table = Mockito.mock(ExternalTable.class);
+ Mockito.when(table.getId()).thenReturn(18L);
+ Mockito.when(table.getName()).thenReturn("common_hudi");
+ Mockito.when(table.getDatabase()).thenReturn(null);
+ Mockito.when(table.getBaseSchema()).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+
+ IncrementalRelation relation = Mockito.mock(IncrementalRelation.class);
+ IncrementalRelation differentRelation =
Mockito.mock(IncrementalRelation.class);
+ LogicalHudiScan scan = newHudiScan(table, relation);
+ LogicalHudiScan sameRelationScan = newHudiScan(table, relation);
+ LogicalHudiScan differentRelationScan = newHudiScan(table,
differentRelation);
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertTrue(comparator.isLogicalEqual(scan,
sameRelationScan));
+ Assertions.assertFalse(comparator.isLogicalEqual(scan,
differentRelationScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentTableSamples() {
+ LogicalOlapScan sampleOneScan = newScanWithTableSample(43,
"common_sample",
+ new org.apache.doris.nereids.trees.TableSample(10, true, 0));
+ LogicalOlapScan sampleTwoScan = newScanWithTableSample(43,
"common_sample",
+ new org.apache.doris.nereids.trees.TableSample(20, true, 0));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(sampleOneScan,
sampleTwoScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentOlapScanParams() {
+ LogicalOlapScan incrementalScan = newScanWithScanParams(44,
"common_params",
+ new TableScanParams(TableScanParams.INCREMENTAL_READ,
+ ImmutableMap.of("end_ts", "10"), ImmutableList.of()));
+ LogicalOlapScan differentIncrementalScan = newScanWithScanParams(44,
"common_params",
+ new TableScanParams(TableScanParams.INCREMENTAL_READ,
+ ImmutableMap.of("end_ts", "20"), ImmutableList.of()));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(incrementalScan,
differentIncrementalScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentStreamReadModes() {
+ LogicalOlapTableStreamScan snapshotScan = newStreamScanWithOffsets(15,
"common_stream", 1000L, 2000L,
+ KeysType.UNIQUE_KEYS, ImmutableMap.of(1L, Pair.of(10L, 20L)))
+ .withReadMode(StreamReadMode.SNAPSHOT);
+ LogicalOlapTableStreamScan resetScan =
snapshotScan.withReadMode(StreamReadMode.RESET);
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(snapshotScan,
resetScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentStreamOffsets() {
+ LogicalOlapTableStreamScan left = newStreamScanWithOffsets(45,
"common_stream_offset", 1000L, 2000L,
+ KeysType.UNIQUE_KEYS, ImmutableMap.of(1L, Pair.of(10L, 20L)));
+ LogicalOlapTableStreamScan right = newStreamScanWithOffsets(45,
"common_stream_offset", 1000L, 2000L,
+ KeysType.UNIQUE_KEYS, ImmutableMap.of(1L, Pair.of(30L, 40L)));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(left, right));
+ }
+
+ @Test
+ void comparatorRejectsDifferentStreamKeysTypes() {
+ LogicalOlapTableStreamScan uniqueKeyScan =
newStreamScanWithOffsets(46, "common_stream_keys", 1000L, 2000L,
+ KeysType.UNIQUE_KEYS, ImmutableMap.of(1L, Pair.of(10L, 20L)));
+ LogicalOlapTableStreamScan dupKeyScan = newStreamScanWithOffsets(46,
"common_stream_keys", 1000L, 2000L,
+ KeysType.DUP_KEYS, ImmutableMap.of(1L, Pair.of(10L, 20L)));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(uniqueKeyScan,
dupKeyScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentStreamIdentities() {
+ LogicalOlapTableStreamScan left = newStreamScanWithOffsets(47,
"common_stream_identity", 1000L, 2000L,
+ KeysType.UNIQUE_KEYS, ImmutableMap.of(1L, Pair.of(10L, 20L)));
+ LogicalOlapTableStreamScan right = newStreamScanWithOffsets(47,
"common_stream_identity", 1000L, 3000L,
+ KeysType.UNIQUE_KEYS, ImmutableMap.of(1L, Pair.of(10L, 20L)));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(left, right));
+ }
+
+ @Test
+ void comparatorRejectsDifferentFileScanSnapshots() {
+ LogicalFileScan versionOneScan = newFileScan(17L,
TableSnapshot.versionOf("1"));
+ LogicalFileScan versionTwoScan = newFileScan(17L,
TableSnapshot.versionOf("2"));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(versionOneScan,
versionTwoScan));
+ }
+
+ @Test
+ void comparatorRejectsDifferentSchemaScanConjuncts() {
+ LogicalSchemaScan baseScan = newSchemaScan(16, "schema_common");
+ LogicalSchemaScan filteredScan =
baseScan.withFrontendConjuncts(Optional.of("ctl"),
+ Optional.of("db"), Optional.of("tbl"),
+ ImmutableList.of(new EqualTo(baseScan.getOutput().get(0),
baseScan.getOutput().get(1))));
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertFalse(comparator.isLogicalEqual(baseScan,
filteredScan));
+ }
+
+ @Test
+ void comparatorAcceptsSameOdbcScanWithDifferentRelationIds() {
+ LogicalOdbcScan left = newOdbcScan(18L, "odbc_common");
+ LogicalOdbcScan right =
left.withRelationId(PlanConstructor.getNextRelationId());
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertTrue(comparator.isLogicalEqual(left, right));
+ }
+
+ @Test
+ void comparatorAcceptsSameTestScan() {
+ LogicalTestScan left = newTestScan(19L, "test_common");
+ LogicalTestScan right = newTestScan(19L, "test_common");
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertTrue(comparator.isLogicalEqual(left, right));
+ }
+
+ @Test
+ void comparatorAcceptsSameScanWithDifferentRelationIds() {
+ LogicalOlapScan left = newScan(13, "common_relation_id");
+ LogicalOlapScan right =
left.withRelationId(PlanConstructor.getNextRelationId());
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertTrue(comparator.isLogicalEqual(left, right));
+ }
+
+ @Test
+ void comparatorAcceptsSameScanWithDifferentTableAliases() {
+ LogicalOlapScan left = newScan(14,
"common_alias").withTableAlias("left_alias");
+ LogicalOlapScan right = newScan(14,
"common_alias").withTableAlias("right_alias");
+
+ PullUpJoinFromUnionAll.LogicalPlanComparator comparator =
+ new PullUpJoinFromUnionAll().new LogicalPlanComparator();
+ Assertions.assertTrue(comparator.isLogicalEqual(left, right));
+ }
+
+ @Test
+ void ruleSkipsJoinChildrenWithDifferentFilteredCommonSideOutputs() {
+ LogicalOlapScan commonSmallScan = newScan(10, "common_small");
+ LogicalOlapScan commonLargeScan = newScan(10, "common_large");
+ LogicalFilter<LogicalOlapScan> commonSmall =
filter(commonSmallScan.withCachedOutput(
+ selectSlotsAsSlots(commonSmallScan.getOutput(), 0)));
+ LogicalFilter<LogicalOlapScan> commonLarge =
filter(commonLargeScan.withCachedOutput(
+ selectSlotsAsSlots(commonLargeScan.getOutput(), 0, 1)));
+ LogicalOlapScan otherLeft = newScan(20, "other_left");
+ LogicalOlapScan otherRight = newScan(30, "other_right");
+
+ LogicalProject<Plan> unionChild1 = outputProject(
+ join(commonSmall, otherLeft),
+ "x", commonSmall.getOutput().get(0),
+ "y", otherLeft.getOutput().get(1));
+ LogicalProject<Plan> unionChild2 = outputProject(
+ join(commonLarge, otherRight),
+ "x", commonLarge.getOutput().get(0),
+ "y", otherRight.getOutput().get(1));
+
+ LogicalUnion union = union(unionChild1, unionChild2);
+
+ Plan rewritten =
PlanChecker.from(MemoTestUtils.createConnectContext(), union)
+ .applyTopDown(new PullUpJoinFromUnionAll())
+ .getPlan();
+
+ Assertions.assertInstanceOf(LogicalUnion.class, rewritten);
+ LogicalUnion rewrittenUnion = (LogicalUnion) rewritten;
+ Assertions.assertEquals(2, rewrittenUnion.children().size());
+ Assertions.assertInstanceOf(LogicalProject.class,
rewrittenUnion.child(0));
+ Assertions.assertInstanceOf(LogicalProject.class,
rewrittenUnion.child(1));
+ }
+
+ @Test
+ void ruleSkipsJoinChildrenWithDifferentCommonSideTabletScopes() {
+ LogicalOlapScan commonLeft = newScan(40,
"common_tablet").withSelectedTabletIds(ImmutableList.of(1L));
+ LogicalOlapScan commonRight = newScan(40,
"common_tablet").withSelectedTabletIds(ImmutableList.of(2L));
+ LogicalOlapScan otherLeft = newScan(41, "other_left_tablet");
+ LogicalOlapScan otherRight = newScan(42, "other_right_tablet");
+
+ LogicalProject<Plan> unionChild1 = outputProject(
+ join(commonLeft, otherLeft),
+ "x", commonLeft.getOutput().get(0),
+ "y", otherLeft.getOutput().get(1));
+ LogicalProject<Plan> unionChild2 = outputProject(
+ join(commonRight, otherRight),
+ "x", commonRight.getOutput().get(0),
+ "y", otherRight.getOutput().get(1));
+
+ LogicalUnion union = union(unionChild1, unionChild2);
+
+ Plan rewritten =
PlanChecker.from(MemoTestUtils.createConnectContext(), union)
+ .applyTopDown(new PullUpJoinFromUnionAll())
+ .getPlan();
+
+ Assertions.assertInstanceOf(LogicalUnion.class, rewritten);
+ }
+
+ private static LogicalOlapScan newScan(long tableId, String tableName) {
+ return PlanConstructor.newLogicalOlapScan(tableId, tableName, 0);
+ }
+
+ private static LogicalOlapScan newPartitionedScan(OlapTable table) {
+ return new LogicalOlapScan(PlanConstructor.getNextRelationId(), table,
ImmutableList.of("db"),
+ ImmutableList.of(), ImmutableList.of(), Optional.empty(),
ImmutableList.of());
+ }
+
+ private static LogicalHudiScan newHudiScan(ExternalTable table,
IncrementalRelation incrementalRelation) {
+ return new TestLogicalHudiScan(PlanConstructor.getNextRelationId(),
table, incrementalRelation);
+ }
+
+ private static LogicalOlapScan newScanWithTableSample(long tableId, String
tableName,
+ org.apache.doris.nereids.trees.TableSample tableSample) {
+ return new LogicalOlapScan(PlanConstructor.getNextRelationId(),
+ PlanConstructor.newOlapTable(tableId, tableName, 0),
ImmutableList.of("db"),
+ ImmutableList.of(), ImmutableList.of(),
Optional.of(tableSample), ImmutableList.of());
+ }
+
+ private static LogicalOlapScan newScanWithScanParams(long tableId, String
tableName, TableScanParams scanParams) {
+ return new LogicalOlapScan(PlanConstructor.getNextRelationId(),
+ PlanConstructor.newOlapTable(tableId, tableName, 0),
ImmutableList.of("db"),
+ ImmutableList.of(), ImmutableList.of(), Optional.empty(),
ImmutableList.of(),
+ Optional.of(scanParams));
+ }
+
+ private static LogicalOlapTableStreamScan newStreamScan(long tableId,
String tableName) {
+ OlapTableStreamWrapper table =
Mockito.mock(OlapTableStreamWrapper.class);
+ Mockito.when(table.getId()).thenReturn(tableId);
+ Mockito.when(table.getDatabase()).thenReturn(null);
+ Mockito.when(table.getName()).thenReturn(tableName);
+
Mockito.when(table.getBaseSchema(true)).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+
Mockito.when(table.getBaseSchema(false)).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+ Mockito.when(table.getBaseSchema()).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+ return new
LogicalOlapTableStreamScan(PlanConstructor.getNextRelationId(),
+ table, ImmutableList.of("db"),
+ ImmutableList.of(), ImmutableList.of(), Optional.empty(),
ImmutableList.of());
+ }
+
+ private static LogicalOlapTableStreamScan newStreamScanWithOffsets(long
tableId, String tableName,
+ long streamDbId, long streamId, KeysType keysType,
ImmutableMap<Long, Pair<Long, Long>> offsets) {
+ OlapTable baseTable = Mockito.mock(OlapTable.class);
+ Mockito.when(baseTable.getId()).thenReturn(tableId);
+ Mockito.when(baseTable.getDatabase()).thenReturn(null);
+ Mockito.when(baseTable.getName()).thenReturn(tableName);
+
+ OlapTableStreamWrapper table =
Mockito.mock(OlapTableStreamWrapper.class);
+ Mockito.when(table.getId()).thenReturn(tableId);
+ Mockito.when(table.getDatabase()).thenReturn(null);
+ Mockito.when(table.getName()).thenReturn(tableName);
+ Mockito.when(table.getBaseTable()).thenReturn(baseTable);
+ Mockito.when(table.getStreamDbId()).thenReturn(streamDbId);
+ Mockito.when(table.getStreamId()).thenReturn(streamId);
+ Mockito.when(table.getStreamKeysType()).thenReturn(keysType);
+
Mockito.when(table.getBaseSchema(true)).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+
Mockito.when(table.getBaseSchema(false)).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+ Mockito.when(table.getBaseSchema()).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+ Mockito.when(table.getOutputUpdateMap()).thenReturn(offsets);
+ return new
LogicalOlapTableStreamScan(PlanConstructor.getNextRelationId(),
+ table, ImmutableList.of("db"), ImmutableList.of(1L),
+ ImmutableList.of(), ImmutableList.of(), Optional.empty(),
ImmutableList.of());
+ }
+
+ private static LogicalFileScan newFileScan(long tableId, TableSnapshot
snapshot) {
+ ExternalTable table = Mockito.mock(ExternalTable.class);
+ Mockito.when(table.getId()).thenReturn(tableId);
+ Mockito.when(table.getDatabase()).thenReturn(null);
+ Mockito.when(table.getName()).thenReturn("ext_common");
+ Mockito.when(table.initSelectedPartitions(Mockito.any()))
+ .thenReturn(LogicalFileScan.SelectedPartitions.NOT_PRUNED);
+ Mockito.when(table.getBaseSchema()).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+ return new LogicalFileScan(new RelationId(1), table,
Collections.singletonList("db"),
+ Collections.emptyList(), Optional.empty(),
Optional.of(snapshot), Optional.empty(), Optional.empty());
+ }
+
+ private static LogicalSchemaScan newSchemaScan(long tableId, String
tableName) {
+ return new LogicalSchemaScan(PlanConstructor.getNextRelationId(),
+ PlanConstructor.newOlapTable(tableId, tableName, 0),
ImmutableList.of("db"));
+ }
+
+ private static LogicalOdbcScan newOdbcScan(long tableId, String tableName)
{
+ OdbcTable table = Mockito.mock(OdbcTable.class);
+ Mockito.when(table.getId()).thenReturn(tableId);
+ Mockito.when(table.getDatabase()).thenReturn(null);
+ Mockito.when(table.getName()).thenReturn(tableName);
+ Mockito.when(table.getBaseSchema()).thenReturn(ImmutableList.of(new
Column("id", Type.INT, true)));
+ return new LogicalOdbcScan(PlanConstructor.getNextRelationId(), table,
ImmutableList.of("db"));
+ }
+
+ private static LogicalTestScan newTestScan(long tableId, String tableName)
{
+ return new LogicalTestScan(PlanConstructor.getNextRelationId(),
+ PlanConstructor.newOlapTable(tableId, tableName, 0),
ImmutableList.of("db"));
+ }
+
+ private static LogicalOlapScan newScanWithExtraIndex(long tableId, String
tableName, long indexId) {
+ OlapTable table = PlanConstructor.newOlapTable(tableId, tableName, 0,
KeysType.DUP_KEYS);
+ table.setIndexMeta(indexId, tableName + "_mv", table.getFullSchema(),
+ 0, 0, (short) 0, org.apache.doris.thrift.TStorageType.COLUMN,
KeysType.DUP_KEYS);
+ return new LogicalOlapScan(PlanConstructor.getNextRelationId(), table,
ImmutableList.of("db"));
+ }
+
+ private static LogicalOlapScan newCachedOutputScan(long tableId, String
tableName, int... indexes) {
+ LogicalOlapScan scan = newScan(tableId, tableName);
+ return scan.withCachedOutput(selectSlotsAsSlots(scan.getOutput(),
indexes));
+ }
+
+ private static LogicalFilter<LogicalOlapScan> filter(LogicalOlapScan
child) {
+ return new LogicalFilter<>(ImmutableSet.of(new
EqualTo(child.getOutput().get(0), child.getOutput().get(0))),
+ child);
+ }
+
+ private static LogicalJoin<Plan, LogicalOlapScan> join(Plan commonSide,
+ LogicalOlapScan otherSide) {
+ Expression joinCondition = new EqualTo(commonSide.getOutput().get(0),
otherSide.getOutput().get(0));
+ return new LogicalJoin<>(JoinType.INNER_JOIN,
ImmutableList.of(joinCondition), ImmutableList.of(),
+ commonSide, otherSide, null);
+ }
+
+ private static LogicalProject<Plan> outputProject(Plan child, String
leftAlias, Slot leftSlot,
+ String rightAlias, Slot rightSlot) {
+ return new LogicalProject<>(ImmutableList.of(
+ new Alias(leftSlot, leftAlias),
+ new Alias(rightSlot, rightAlias)), child);
+ }
+
+ private static LogicalUnion union(LogicalProject<Plan> left,
LogicalProject<Plan> right) {
+ ImmutableList.Builder<NamedExpression> outputs =
ImmutableList.builder();
+ for (Slot slot : left.getOutput()) {
+ outputs.add(new SlotReference(slot.getName(), slot.getDataType(),
slot.nullable()));
+ }
+ return new LogicalUnion(Qualifier.ALL, outputs.build(),
ImmutableList.of(
+ toSlotReferences(left.getOutput()),
+ toSlotReferences(right.getOutput())), ImmutableList.of(),
false, ImmutableList.of(left, right));
+ }
+
+ private static LogicalProject<LogicalOlapScan>
project(List<NamedExpression> outputs, LogicalOlapScan child) {
+ return new LogicalProject<>(outputs, child);
+ }
+
+ private static List<NamedExpression> selectSlots(List<Slot> output, int...
indexes) {
+ ImmutableList.Builder<NamedExpression> selected =
ImmutableList.builder();
+ for (int index : indexes) {
+ selected.add(output.get(index));
+ }
+ return selected.build();
+ }
+
+ private static List<Slot> selectSlotsAsSlots(List<Slot> output, int...
indexes) {
+ ImmutableList.Builder<Slot> selected = ImmutableList.builder();
+ for (int index : indexes) {
+ selected.add(output.get(index));
+ }
+ return selected.build();
+ }
+
+ private static List<SlotReference> toSlotReferences(List<Slot> slots) {
+ ImmutableList.Builder<SlotReference> references =
ImmutableList.builder();
+ for (Slot slot : slots) {
+ references.add((SlotReference) slot);
+ }
+ return references.build();
+ }
+
+ private static class TestLogicalHudiScan extends LogicalHudiScan {
+ private TestLogicalHudiScan(RelationId id, ExternalTable table,
IncrementalRelation incrementalRelation) {
+ super(id, table, ImmutableList.of("db"),
LogicalFileScan.SelectedPartitions.NOT_PRUNED,
+ Optional.empty(), Optional.empty(), Optional.empty(),
Optional.of(incrementalRelation),
+ ImmutableList.of(), ImmutableList.of(), Optional.empty(),
Optional.empty(), "", Optional.empty());
+ }
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]