feat: Add optimizer rule to rewrite Left Join to Inner Join
This commit introduces a new optimizer transformation rule, `RewriteLeftJoinToInnerJoinRule`. This rule identifies `LEFT OUTER JOIN` operations that can be safely converted to `INNER JOIN` operations. The conditions for this transformation are: 1. A foreign key constraint exists from the left table to the right table involved in the join. 2. All columns on the left table that are part of the foreign key constraint are defined as NOT NULL. 3. All columns participating in the foreign key constraint are part of an equality predicate in the join's ON condition. This optimization can lead to more efficient query plans by allowing the optimizer to consider a wider range of join strategies and by enabling further optimizations like table pruning for the right-hand side of the original join. The rule has been integrated into the logical optimization phase and includes unit tests to cover various scenarios, including those mentioned in issue #59101. This change is expected to improve performance for queries and materialized views that meet the specified criteria.
This commit is contained in:
parent
1f6223511a
commit
d4cbea0ab7
|
|
@ -28,6 +28,7 @@ import com.starrocks.sql.optimizer.operator.OperatorType;
|
|||
import com.starrocks.sql.optimizer.operator.logical.LogicalJoinOperator;
|
||||
import com.starrocks.sql.optimizer.operator.logical.LogicalOlapScanOperator;
|
||||
import com.starrocks.sql.optimizer.operator.pattern.Pattern;
|
||||
import com.starrocks.analysis.BinaryType;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.BinaryPredicateOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.ColumnRefOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.ScalarOperator;
|
||||
|
|
@ -48,12 +49,10 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
@Override
|
||||
public boolean check(OptExpression input, OptimizerContext context) {
|
||||
LogicalJoinOperator joinOperator = (LogicalJoinOperator) input.getOp();
|
||||
// This rule only applies to LEFT OUTER JOIN
|
||||
if (joinOperator.getJoinType() != JoinOperator.LEFT_OUTER_JOIN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the ON predicate involves a foreign key and the foreign key columns are NOT NULL.
|
||||
LogicalOlapScanOperator leftScan = (LogicalOlapScanOperator) input.inputAt(0).getOp();
|
||||
LogicalOlapScanOperator rightScan = (LogicalOlapScanOperator) input.inputAt(1).getOp();
|
||||
|
||||
|
|
@ -65,7 +64,6 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
}
|
||||
|
||||
OlapTable leftOlapTable = (OlapTable) leftTable;
|
||||
|
||||
List<ForeignKeyConstraint> fkConstraints = leftOlapTable.getForeignKeyConstraints();
|
||||
if (fkConstraints == null || fkConstraints.isEmpty()) {
|
||||
return false;
|
||||
|
|
@ -74,20 +72,18 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
List<ScalarOperator> onPredicates = Utils.extractConjuncts(joinOperator.getOnPredicate());
|
||||
|
||||
for (ForeignKeyConstraint fk : fkConstraints) {
|
||||
if (fk.getParentTableInfo() == null) {
|
||||
if (fk.getParentTableInfo() == null || fk.getParentTableInfo().getTableId() == 0) {
|
||||
continue;
|
||||
}
|
||||
// Check if the referenced table in FK is the right table of the join
|
||||
if (fk.getParentTableInfo().getTableId() != rightTable.getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Map column IDs from FK constraint to ColumnRefOperators in the ON predicate
|
||||
Map<Integer, ColumnRefOperator> leftJoinColumnRefs = leftScan.getColRefToColumnMetaMap().entrySet().stream()
|
||||
.collect(Collectors.toMap(e -> e.getValue().getColumnId().asInt(), Map.Entry::getKey));
|
||||
Map<Integer, ColumnRefOperator> rightJoinColumnRefs = rightScan.getColRefToColumnMetaMap().entrySet().stream()
|
||||
.collect(Collectors.toMap(e -> e.getValue().getColumnId().asInt(), Map.Entry::getKey));
|
||||
|
||||
|
||||
boolean allFkColumnsMatchOnPredicate = true;
|
||||
boolean allFkColumnsNotNull = true;
|
||||
|
||||
|
|
@ -96,20 +92,19 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
ColumnRefOperator rightFkColRef = rightJoinColumnRefs.get(fkColPair.second.asInt());
|
||||
|
||||
if (leftFkColRef == null || rightFkColRef == null) {
|
||||
allFkColumnsMatchOnPredicate = false; // Should not happen if FK is valid
|
||||
allFkColumnsMatchOnPredicate = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if this FK pair is part of the ON predicate
|
||||
boolean foundInOnPredicate = false;
|
||||
for (ScalarOperator onConjunct : onPredicates) {
|
||||
if (onConjunct instanceof BinaryPredicateOperator) {
|
||||
BinaryPredicateOperator binaryPred = (BinaryPredicateOperator) onConjunct;
|
||||
if (binaryPred.getBinaryType() == BinaryPredicateOperator.BinaryType.EQ) {
|
||||
if (binaryPred.getBinaryType() == BinaryType.EQ) { // Corrected enum usage
|
||||
ScalarOperator predChild0 = binaryPred.getChild(0);
|
||||
ScalarOperator predChild1 = binaryPred.getChild(1);
|
||||
if ((predChild0.equals(leftFkColRef) && predChild1.equals(rightFkColRef)) ||
|
||||
(predChild0.equals(rightFkColRef) && predChild1.equals(leftFkColRef))) {
|
||||
(predChild0.equals(rightFkColRef) && predChild1.equals(leftFkColRef))) {
|
||||
foundInOnPredicate = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -121,7 +116,6 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
break;
|
||||
}
|
||||
|
||||
// Check if the left FK column is NOT NULL
|
||||
Column leftColumn = leftOlapTable.getColumn(fkColPair.first);
|
||||
if (leftColumn == null || leftColumn.isAllowNull()) {
|
||||
allFkColumnsNotNull = false;
|
||||
|
|
@ -130,7 +124,7 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
}
|
||||
|
||||
if (allFkColumnsMatchOnPredicate && allFkColumnsNotNull) {
|
||||
return true; // Conditions met for one of the FK constraints
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -143,11 +137,9 @@ public class RewriteLeftJoinToInnerJoinRule extends TransformationRule {
|
|||
LogicalJoinOperator.Builder builder = new LogicalJoinOperator.Builder();
|
||||
builder.withOperator(currentJoinOperator);
|
||||
builder.setJoinType(JoinOperator.INNER_JOIN);
|
||||
// The ON predicate remains the same
|
||||
builder.setOnPredicate(currentJoinOperator.getOnPredicate());
|
||||
builder.setOriginalOnPredicate(currentJoinOperator.getOriginalOnPredicate());
|
||||
|
||||
|
||||
OptExpression result = OptExpression.create(builder.build(), input.getInputs());
|
||||
return Lists.newArrayList(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ package com.starrocks.sql.optimizer.rule.transformation;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.starrocks.analysis.BinaryType;
|
||||
import com.starrocks.analysis.JoinOperator;
|
||||
import com.starrocks.catalog.BaseTableInfo;
|
||||
import com.starrocks.catalog.Column;
|
||||
import com.starrocks.catalog.ColumnId;
|
||||
import com.starrocks.catalog.DistributionInfo;
|
||||
|
|
@ -62,39 +64,119 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
public class RewriteLeftJoinToInnerJoinRuleTest {
|
||||
|
||||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed 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
|
||||
//
|
||||
// https://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 com.starrocks.sql.optimizer.rule.transformation;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.starrocks.analysis.BinaryType;
|
||||
import com.starrocks.analysis.JoinOperator;
|
||||
import com.starrocks.catalog.BaseTableInfo;
|
||||
import com.starrocks.catalog.Column;
|
||||
import com.starrocks.catalog.ColumnId;
|
||||
import com.starrocks.catalog.DistributionInfo;
|
||||
import com.starrocks.catalog.ForeignKeyConstraint;
|
||||
import com.starrocks.catalog.HashDistributionInfo;
|
||||
import com.starrocks.catalog.KeysType;
|
||||
import com.starrocks.catalog.MaterializedIndex;
|
||||
import com.starrocks.catalog.OlapTable;
|
||||
import com.starrocks.catalog.PartitionInfo;
|
||||
import com.starrocks.catalog.ScalarType;
|
||||
import com.starrocks.catalog.Type;
|
||||
import com.starrocks.common.Pair;
|
||||
import com.starrocks.sql.optimizer.Memo;
|
||||
import com.starrocks.sql.optimizer.OptExpression;
|
||||
import com.starrocks.sql.optimizer.OptimizerContext;
|
||||
import com.starrocks.sql.optimizer.base.ColumnRefFactory;
|
||||
import com.starrocks.sql.optimizer.base.DistributionSpec;
|
||||
import com.starrocks.sql.optimizer.operator.Projection;
|
||||
import com.starrocks.sql.optimizer.operator.logical.LogicalJoinOperator;
|
||||
import com.starrocks.sql.optimizer.operator.logical.LogicalOlapScanOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.BinaryPredicateOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.ColumnRefOperator;
|
||||
import com.starrocks.sql.optimizer.operator.scalar.ScalarOperator;
|
||||
import com.starrocks.sql.optimizer.task.TaskContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
public class RewriteLeftJoinToInnerJoinRuleTest {
|
||||
|
||||
private OptimizerContext optimizerContext;
|
||||
private ColumnRefFactory columnRefFactory;
|
||||
private static final long MOCK_DB_ID = 1L;
|
||||
private static final String MOCK_DB_NAME = "test_db";
|
||||
private Map<Long, OlapTable> mockTables = new HashMap<>();
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
columnRefFactory = new ColumnRefFactory();
|
||||
optimizerContext = new OptimizerContext(new Memo(), columnRefFactory, new TaskContext(null, null, null));
|
||||
// Mock GlobalStateMgr and GlobalConstraintManager if necessary,
|
||||
// For this rule, direct OlapTable.getForeignKeyConstraints() is used.
|
||||
mockTables.clear(); // Clear for each test to ensure isolation
|
||||
}
|
||||
|
||||
private OlapTable createMockTable(String name,
|
||||
List<Column> columns,
|
||||
List<ColumnId> pkColumnIds,
|
||||
List<ForeignKeyConstraint> fkConstraints) {
|
||||
long tableId = System.nanoTime(); // Quick way to get unique enough IDs for tests
|
||||
List<ForeignKeyConstraint> fkConstraints,
|
||||
long tableId) {
|
||||
MaterializedIndex baseIndex = new MaterializedIndex(1, MaterializedIndex.IndexState.NORMAL);
|
||||
PartitionInfo partitionInfo = new PartitionInfo(); // Dummy partition info
|
||||
DistributionInfo distributionInfo = new HashDistributionInfo(10, pkColumnIds.stream()
|
||||
PartitionInfo partitionInfo = new PartitionInfo();
|
||||
List<String> pkColNames = pkColumnIds.stream()
|
||||
.map(cid -> columns.stream().filter(c -> c.getColumnId().equals(cid)).findFirst().get().getName())
|
||||
.collect(Collectors.toList())); // Dummy distribution
|
||||
.collect(Collectors.toList());
|
||||
DistributionInfo distributionInfo = new HashDistributionInfo(10, pkColNames);
|
||||
|
||||
OlapTable table = new OlapTable(tableId, name, columns, KeysType.PRIMARY_KEYS, partitionInfo, distributionInfo);
|
||||
table.setIndexMeta(1L, name, columns, 0, 0, (short) pkColumnIds.size(), KeysType.PRIMARY_KEYS, null);
|
||||
table.setBaseIndexId(baseIndex.getId());
|
||||
|
||||
if (fkConstraints != null) {
|
||||
for (ForeignKeyConstraint fk : fkConstraints) {
|
||||
if (fk.getParentTableInfo() != null && fk.getParentTableInfo().getTableId() != 0) {
|
||||
OlapTable parentTable = findMockTableById(fk.getParentTableInfo().getTableId());
|
||||
if (parentTable != null) {
|
||||
BaseTableInfo parentBaseTableInfo = new BaseTableInfo(MOCK_DB_ID, parentTable.getId(),
|
||||
MOCK_DB_NAME, parentTable.getName());
|
||||
fk.setParentTableInfo(parentBaseTableInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
table.setForeignKeyConstraints(fkConstraints);
|
||||
}
|
||||
mockTables.put(tableId, table);
|
||||
return table;
|
||||
}
|
||||
|
||||
private OlapTable findMockTableById(long tableId) {
|
||||
return mockTables.get(tableId);
|
||||
}
|
||||
|
||||
private Column createColumn(String name, Type type, boolean isAllowNull, boolean isKey, String colId) {
|
||||
Column col = new Column(name, type, isKey);
|
||||
col.setIsAllowNull(isAllowNull);
|
||||
|
|
@ -102,7 +184,8 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
return col;
|
||||
}
|
||||
|
||||
private LogicalOlapScanOperator createScanOperator(OlapTable table, List<String> selectedColNames) {
|
||||
private Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> createScanOperator(OlapTable table,
|
||||
List<String> selectedColNames) {
|
||||
Map<ColumnRefOperator, Column> colRefToColumnMeta = new HashMap<>();
|
||||
Map<Column, ColumnRefOperator> columnMetaToColRef = new HashMap<>();
|
||||
List<ColumnRefOperator> outputVariables = new ArrayList<>();
|
||||
|
|
@ -116,26 +199,26 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
outputVariables.add(colRef);
|
||||
}
|
||||
|
||||
return LogicalOlapScanOperator.builder()
|
||||
LogicalOlapScanOperator scanOp = LogicalOlapScanOperator.builder()
|
||||
.setTable(table)
|
||||
.setColRefToColumnMetaMap(colRefToColumnMeta)
|
||||
.setColumnMetaToColRefMap(columnMetaToColRef)
|
||||
.setOutputColumnRefOp(outputVariables)
|
||||
.setDistributionSpec(DistributionSpec.createAnyDistributionSpec())
|
||||
.setLimit(Operator.DEFAULT_LIMIT)
|
||||
.setLimit(com.starrocks.sql.optimizer.operator.Operator.DEFAULT_LIMIT)
|
||||
.setPredicate(null)
|
||||
.setSelectedIndexId(table.getBaseIndexId())
|
||||
.setProjection(new Projection(Maps.newHashMap()))
|
||||
.build();
|
||||
return Pair.create(scanOp, columnMetaToColRef);
|
||||
}
|
||||
|
||||
|
||||
private LogicalJoinOperator createJoinOperator(OptExpression leftChild, OptExpression rightChild,
|
||||
JoinOperator joinType, ScalarOperator onPredicate) {
|
||||
return LogicalJoinOperator.builder()
|
||||
.setJoinType(joinType)
|
||||
.setOnPredicate(onPredicate)
|
||||
.setProjection(null) // Not strictly needed for this rule test
|
||||
.setProjection(null)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -143,37 +226,44 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
RewriteLeftJoinToInnerJoinRule rule = new RewriteLeftJoinToInnerJoinRule();
|
||||
List<OptExpression> result = rule.transform(joinExpression, optimizerContext);
|
||||
if (result.isEmpty()) {
|
||||
return joinExpression; // Rule didn't fire or returned empty
|
||||
return joinExpression;
|
||||
}
|
||||
return result.get(0);
|
||||
}
|
||||
|
||||
// Test Case 1: Basic Transformation
|
||||
@Test
|
||||
public void testBasicTransformation() {
|
||||
// t1 (pk1 INT PK)
|
||||
Column t1_pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1), ImmutableList.of(t1_pk1.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
// t2 (fk2 INT NOT NULL, FK(fk2) refs t1(pk1))
|
||||
Column t2_fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1"); // Treat as key for simplicity
|
||||
Column t2_val = createColumn("val", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
Column t1Pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1), ImmutableList.of(t1Pk1.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2Fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2Val = createColumn("val", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_fk2.getColumnId(), t1_pk1.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2Fk2.getColumnId(), t1Pk1.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2, t2_val), ImmutableList.of(t2_fk2.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2, t2Val), ImmutableList.of(t2Fk2.getColumnId()),
|
||||
ImmutableList.of(fk), t2Id);
|
||||
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2", "val"));
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2", "val"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1_pk1_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2_ref = scanT2.getOutputVariables().get(0);
|
||||
ColumnRefOperator t1Pk1Ref = scanT1ColMap.get(t1Pk1);
|
||||
ColumnRefOperator t2Fk2Ref = scanT2ColMap.get(t2Fk2);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2_ref, t1_pk1_ref);
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2Ref, t1Pk1Ref);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -183,26 +273,37 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.INNER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 2: Condition Not Met - FK Column is Nullable
|
||||
@Test
|
||||
public void testFkColumnNullable() {
|
||||
Column t1_pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1), ImmutableList.of(t1_pk1.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_fk2 = createColumn("fk2", ScalarType.INT, true, true, "t2_c1"); // ALLOW NULL
|
||||
Column t2_val = createColumn("val", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
Column t1Pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1), ImmutableList.of(t1Pk1.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2Fk2 = createColumn("fk2", ScalarType.INT, true, true, "t2_c1"); // ALLOW NULL
|
||||
Column t2Val = createColumn("val", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_fk2.getColumnId(), t1_pk1.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2Fk2.getColumnId(), t1Pk1.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2, t2_val), ImmutableList.of(t2_fk2.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2, t2Val), ImmutableList.of(t2Fk2.getColumnId()),
|
||||
ImmutableList.of(fk), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2", "val"));
|
||||
ColumnRefOperator t1_pk1_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2_ref = scanT2.getOutputVariables().get(0);
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2_ref, t1_pk1_ref);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2", "val"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1Pk1Ref = scanT1ColMap.get(t1Pk1);
|
||||
ColumnRefOperator t2Fk2Ref = scanT2ColMap.get(t2Fk2);
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2Ref, t1Pk1Ref);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -211,21 +312,31 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.LEFT_OUTER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 3: Condition Not Met - No Foreign Key Constraint
|
||||
@Test
|
||||
public void testNoForeignKeyConstraint() {
|
||||
Column t1_pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1), ImmutableList.of(t1_pk1.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1"); // NOT NULL
|
||||
Column t2_val = createColumn("val", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2, t2_val), ImmutableList.of(t2_fk2.getColumnId()), null); // No FK
|
||||
Column t1Pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1), ImmutableList.of(t1Pk1.getColumnId()), null, t1Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2", "val"));
|
||||
ColumnRefOperator t1_pk1_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2_ref = scanT2.getOutputVariables().get(0);
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2_ref, t1_pk1_ref);
|
||||
Column t2Fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1"); // NOT NULL
|
||||
Column t2Val = createColumn("val", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2, t2Val), ImmutableList.of(t2Fk2.getColumnId()), null, t2Id);
|
||||
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2", "val"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1Pk1Ref = scanT1ColMap.get(t1Pk1);
|
||||
ColumnRefOperator t2Fk2Ref = scanT2ColMap.get(t2Fk2);
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2Ref, t1Pk1Ref);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -234,27 +345,38 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.LEFT_OUTER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 4: Condition Not Met - Join Predicate Mismatch
|
||||
@Test
|
||||
public void testJoinPredicateMismatch() {
|
||||
Column t1_pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1), ImmutableList.of(t1_pk1.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2_other_col = createColumn("other_col", ScalarType.INT, false, false, "t2_c2");
|
||||
Column t1Pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1), ImmutableList.of(t1Pk1.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2Fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2OtherCol = createColumn("other_col", ScalarType.INT, false, false, "t2_c2");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_fk2.getColumnId(), t1_pk1.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2Fk2.getColumnId(), t1Pk1.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2, t2_other_col), ImmutableList.of(t2_fk2.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2, t2OtherCol),
|
||||
ImmutableList.of(t2Fk2.getColumnId()), ImmutableList.of(fk), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2", "other_col"));
|
||||
ColumnRefOperator t1_pk1_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_other_col_ref = scanT2.getOutputVariables().get(1); // Using other_col
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_other_col_ref, t1_pk1_ref);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2", "other_col"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1Pk1Ref = scanT1ColMap.get(t1Pk1);
|
||||
ColumnRefOperator t2OtherColRef = scanT2ColMap.get(t2OtherCol);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2OtherColRef, t1Pk1Ref);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -263,33 +385,43 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.LEFT_OUTER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 5: Condition Not Met - Join Predicate Incomplete for Composite FK
|
||||
@Test
|
||||
public void testCompositeFkIncompletePredicate() {
|
||||
Column t1_pk1a = createColumn("pk1a", ScalarType.INT, false, true, "t1_c1");
|
||||
Column t1_pk1b = createColumn("pk1b", ScalarType.INT, false, true, "t1_c2");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1a, t1_pk1b),
|
||||
ImmutableList.of(t1_pk1a.getColumnId(), t1_pk1b.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_fk2a = createColumn("fk2a", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2_fk2b = createColumn("fk2b", ScalarType.INT, false, true, "t2_c2");
|
||||
Column t1Pk1a = createColumn("pk1a", ScalarType.INT, false, true, "t1_c1");
|
||||
Column t1Pk1b = createColumn("pk1b", ScalarType.INT, false, true, "t1_c2");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1a, t1Pk1b),
|
||||
ImmutableList.of(t1Pk1a.getColumnId(), t1Pk1b.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2Fk2a = createColumn("fk2a", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2Fk2b = createColumn("fk2b", ScalarType.INT, false, true, "t2_c2");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1_comp",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(
|
||||
Pair.create(t2_fk2a.getColumnId(), t1_pk1a.getColumnId()),
|
||||
Pair.create(t2_fk2b.getColumnId(), t1_pk1b.getColumnId())
|
||||
Pair.create(t2Fk2a.getColumnId(), t1Pk1a.getColumnId()),
|
||||
Pair.create(t2Fk2b.getColumnId(), t1Pk1b.getColumnId())
|
||||
)
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2a, t2_fk2b),
|
||||
ImmutableList.of(t2_fk2a.getColumnId(), t2_fk2b.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2a, t2Fk2b),
|
||||
ImmutableList.of(t2Fk2a.getColumnId(), t2Fk2b.getColumnId()), ImmutableList.of(fk), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1a", "pk1b"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2a", "fk2b"));
|
||||
ColumnRefOperator t1_pk1a_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2a_ref = scanT2.getOutputVariables().get(0);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1a", "pk1b"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2a_ref, t1_pk1a_ref); // Only one part
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2a", "fk2b"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1Pk1aRef = scanT1ColMap.get(t1Pk1a);
|
||||
ColumnRefOperator t2Fk2aRef = scanT2ColMap.get(t2Fk2a);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2aRef, t1Pk1aRef);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -298,36 +430,46 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.LEFT_OUTER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 6: Transformation with Composite FK
|
||||
@Test
|
||||
public void testCompositeFkCompletePredicate() {
|
||||
Column t1_pk1a = createColumn("pk1a", ScalarType.INT, false, true, "t1_c1");
|
||||
Column t1_pk1b = createColumn("pk1b", ScalarType.INT, false, true, "t1_c2");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1a, t1_pk1b),
|
||||
ImmutableList.of(t1_pk1a.getColumnId(), t1_pk1b.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_fk2a = createColumn("fk2a", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2_fk2b = createColumn("fk2b", ScalarType.INT, false, true, "t2_c2");
|
||||
Column t1Pk1a = createColumn("pk1a", ScalarType.INT, false, true, "t1_c1");
|
||||
Column t1Pk1b = createColumn("pk1b", ScalarType.INT, false, true, "t1_c2");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1a, t1Pk1b),
|
||||
ImmutableList.of(t1Pk1a.getColumnId(), t1Pk1b.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2Fk2a = createColumn("fk2a", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t2Fk2b = createColumn("fk2b", ScalarType.INT, false, true, "t2_c2");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1_comp",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(
|
||||
Pair.create(t2_fk2a.getColumnId(), t1_pk1a.getColumnId()),
|
||||
Pair.create(t2_fk2b.getColumnId(), t1_pk1b.getColumnId())
|
||||
Pair.create(t2Fk2a.getColumnId(), t1Pk1a.getColumnId()),
|
||||
Pair.create(t2Fk2b.getColumnId(), t1Pk1b.getColumnId())
|
||||
)
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2a, t2_fk2b),
|
||||
ImmutableList.of(t2_fk2a.getColumnId(), t2_fk2b.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2a, t2Fk2b),
|
||||
ImmutableList.of(t2Fk2a.getColumnId(), t2Fk2b.getColumnId()), ImmutableList.of(fk), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1a", "pk1b"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2a", "fk2b"));
|
||||
ColumnRefOperator t1_pk1a_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t1_pk1b_ref = scanT1.getOutputVariables().get(1);
|
||||
ColumnRefOperator t2_fk2a_ref = scanT2.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2b_ref = scanT2.getOutputVariables().get(1);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1a", "pk1b"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ScalarOperator pred1 = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2a_ref, t1_pk1a_ref);
|
||||
ScalarOperator pred2 = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2b_ref, t1_pk1b_ref);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2a", "fk2b"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1Pk1aRef = scanT1ColMap.get(t1Pk1a);
|
||||
ColumnRefOperator t1Pk1bRef = scanT1ColMap.get(t1Pk1b);
|
||||
ColumnRefOperator t2Fk2aRef = scanT2ColMap.get(t2Fk2a);
|
||||
ColumnRefOperator t2Fk2bRef = scanT2ColMap.get(t2Fk2b);
|
||||
|
||||
ScalarOperator pred1 = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2aRef, t1Pk1aRef);
|
||||
ScalarOperator pred2 = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2bRef, t1Pk1bRef);
|
||||
ScalarOperator onPredicate = BinaryPredicateOperator.and(pred1, pred2);
|
||||
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
|
|
@ -338,33 +480,41 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.INNER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 7: GitHub Issue Example (Case 1 from issue #59101)
|
||||
@Test
|
||||
public void testGitHubIssue59101Case1() {
|
||||
// t1 (cust_code PK)
|
||||
Column t1_cust_code = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_cust_code), ImmutableList.of(t1_cust_code.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
// t2 (cust_code FK to t1.cust_code, cust_code in t2 is NOT NULL)
|
||||
Column t2_cust_code = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t2_c1"); // FK, NOT NULL
|
||||
Column t2_ivst_prtcp_id = createColumn("ivst_prtcp_id", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
Column t2_one_id = createColumn("one_id", ScalarType.VARCHAR, true, false, "t2_c3");
|
||||
Column t1CustCode = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1CustCode),
|
||||
ImmutableList.of(t1CustCode.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2CustCode = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t2_c1");
|
||||
Column t2IvstPrtcpId = createColumn("ivst_prtcp_id", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
Column t2OneId = createColumn("one_id", ScalarType.VARCHAR, true, false, "t2_c3");
|
||||
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1_cust_code",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_cust_code.getColumnId(), t1_cust_code.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2CustCode.getColumnId(), t1CustCode.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_cust_code, t2_ivst_prtcp_id, t2_one_id),
|
||||
ImmutableList.of(t2_cust_code.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2CustCode, t2IvstPrtcpId, t2OneId),
|
||||
ImmutableList.of(t2CustCode.getColumnId()), ImmutableList.of(fk), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("cust_code"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("cust_code", "ivst_prtcp_id", "one_id"));
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("cust_code"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ColumnRefOperator t1_cust_code_ref = scanT1.getOutputVariables().get(0); // b.cust_code
|
||||
ColumnRefOperator t2_cust_code_ref = scanT2.getOutputVariables().get(0); // a.cust_code
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("cust_code", "ivst_prtcp_id", "one_id"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_cust_code_ref, t1_cust_code_ref);
|
||||
ColumnRefOperator t1CustCodeRef = scanT1ColMap.get(t1CustCode);
|
||||
ColumnRefOperator t2CustCodeRef = scanT2ColMap.get(t2CustCode);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2CustCodeRef, t1CustCodeRef);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -373,82 +523,90 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.INNER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
// Test Case 8: Table Pruning Interaction (Inspired by Case 2 from issue #59101)
|
||||
@Test
|
||||
public void testTablePruningInteractionFocusJoinType() {
|
||||
// Setup similar to Test Case 7
|
||||
Column t1_cust_code = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_cust_code), ImmutableList.of(t1_cust_code.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_cust_code = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t2_c1");
|
||||
Column t2_ivst_prtcp_id = createColumn("ivst_prtcp_id", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
Column t1CustCode = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1CustCode),
|
||||
ImmutableList.of(t1CustCode.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2CustCode = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t2_c1");
|
||||
Column t2IvstPrtcpId = createColumn("ivst_prtcp_id", ScalarType.VARCHAR, true, false, "t2_c2");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1_cust_code",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_cust_code.getColumnId(), t1_cust_code.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2CustCode.getColumnId(), t1CustCode.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_cust_code, t2_ivst_prtcp_id),
|
||||
ImmutableList.of(t2_cust_code.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2CustCode, t2IvstPrtcpId),
|
||||
ImmutableList.of(t2CustCode.getColumnId()), ImmutableList.of(fk), t2Id);
|
||||
|
||||
// Query: SELECT a.ivst_prtcp_id FROM t2 a LEFT JOIN t1 b ON a.cust_code = b.cust_code
|
||||
// Scan t1 for cust_code (needed for join), scan t2 for cust_code and ivst_prtcp_id
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("cust_code"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("cust_code", "ivst_prtcp_id"));
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("cust_code"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ColumnRefOperator t1_cust_code_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_cust_code_ref = scanT2.getOutputVariables().get(0);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("cust_code", "ivst_prtcp_id"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_cust_code_ref, t1_cust_code_ref);
|
||||
ColumnRefOperator t1CustCodeRef = scanT1ColMap.get(t1CustCode);
|
||||
ColumnRefOperator t2CustCodeRef = scanT2ColMap.get(t2CustCode);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2CustCodeRef, t1CustCodeRef);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
||||
// Apply RewriteLeftJoinToInnerJoinRule
|
||||
OptExpression transformedExpr = applyRule(joinExpr);
|
||||
assertEquals(JoinOperator.INNER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType(),
|
||||
"Join should be transformed to INNER JOIN first.");
|
||||
|
||||
// Conceptually, pruning would happen next.
|
||||
// For this unit test, we focus on the join type change.
|
||||
// A more integrated test would apply PruneJoinColumns (or similar) and check scanT1's outputs.
|
||||
// If PruneJoinColumns rule were applied here (and assuming SELECT a.ivst_prtcp_id means only t2_ivst_prtcp_id_ref is needed from join output)
|
||||
// then scanT1 might be pruned if it's not needed for filtering or its output columns would be empty.
|
||||
// This is hard to test in isolation without running a fuller optimizer sequence.
|
||||
}
|
||||
|
||||
|
||||
// Test Case 9: Materialized View Definition
|
||||
@Test
|
||||
public void testMaterializedViewDefinition() {
|
||||
// Setup similar to Test Case 7
|
||||
Column t1_cust_code = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t1_c1_mv");
|
||||
OlapTable t1 = createMockTable("t1_mv", ImmutableList.of(t1_cust_code), ImmutableList.of(t1_cust_code.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_cust_code = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t2_c1_mv");
|
||||
Column t2_ivst_prtcp_id = createColumn("ivst_prtcp_id", ScalarType.VARCHAR, true, false, "t2_c2_mv");
|
||||
Column t1CustCode = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t1_c1_mv");
|
||||
OlapTable t1 = createMockTable("t1_mv", ImmutableList.of(t1CustCode),
|
||||
ImmutableList.of(t1CustCode.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2CustCode = createColumn("cust_code", ScalarType.VARCHAR, false, true, "t2_c1_mv");
|
||||
Column t2IvstPrtcpId = createColumn("ivst_prtcp_id", ScalarType.VARCHAR, true, false, "t2_c2_mv");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1_mv_cust_code",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_cust_code.getColumnId(), t1_cust_code.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2CustCode.getColumnId(), t1CustCode.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2_mv", ImmutableList.of(t2_cust_code, t2_ivst_prtcp_id),
|
||||
ImmutableList.of(t2_cust_code.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2_mv", ImmutableList.of(t2CustCode, t2IvstPrtcpId),
|
||||
ImmutableList.of(t2CustCode.getColumnId()), ImmutableList.of(fk), t2Id);
|
||||
|
||||
// MV: SELECT b.cust_code, a.ivst_prtcp_id FROM t2 a LEFT JOIN t1 b ON a.cust_code = b.cust_code
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("cust_code"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("cust_code", "ivst_prtcp_id"));
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("cust_code"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ColumnRefOperator t1_cust_code_ref = scanT1.getOutputVariables().get(0); // b.cust_code
|
||||
ColumnRefOperator t2_cust_code_ref = scanT2.getOutputVariables().get(0); // a.cust_code
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("cust_code", "ivst_prtcp_id"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_cust_code_ref, t1_cust_code_ref);
|
||||
ColumnRefOperator t1CustCodeRef = scanT1ColMap.get(t1CustCode);
|
||||
ColumnRefOperator t2CustCodeRef = scanT2ColMap.get(t2CustCode);
|
||||
ColumnRefOperator t2IvstPrtcpIdRef = scanT2ColMap.get(t2IvstPrtcpId);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2CustCodeRef, t1CustCodeRef);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
|
||||
// Simulate projection for MV: b.cust_code, a.ivst_prtcp_id
|
||||
Map<ColumnRefOperator, ScalarOperator> mvProjectionMap = Maps.newHashMap();
|
||||
mvProjectionMap.put(columnRefFactory.create("output_b_cust_code", t1_cust_code_ref.getType(), t1_cust_code_ref.isNullable()), t1_cust_code_ref);
|
||||
mvProjectionMap.put(columnRefFactory.create("output_a_ivst_prtcp_id", scanT2.getOutputVariables().get(1).getType(), scanT2.getOutputVariables().get(1).isNullable()), scanT2.getOutputVariables().get(1));
|
||||
mvProjectionMap.put(columnRefFactory.create("output_b_cust_code", t1CustCodeRef.getType(),
|
||||
t1CustCodeRef.isNullable()), t1CustCodeRef);
|
||||
mvProjectionMap.put(columnRefFactory.create("output_a_ivst_prtcp_id", t2IvstPrtcpIdRef.getType(),
|
||||
t2IvstPrtcpIdRef.isNullable()), t2IvstPrtcpIdRef);
|
||||
joinOp.setProjection(new Projection(mvProjectionMap));
|
||||
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
@ -458,67 +616,90 @@ public class RewriteLeftJoinToInnerJoinRuleTest {
|
|||
assertEquals(JoinOperator.INNER_JOIN, ((LogicalJoinOperator) transformedExpr.getOp()).getJoinType());
|
||||
assertNotNull(((LogicalJoinOperator) transformedExpr.getOp()).getProjection(), "Projection should be preserved");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNotLeftOuterJoin() {
|
||||
Column t1_pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1), ImmutableList.of(t1_pk1.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
|
||||
Column t2_fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
Column t1Pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1), ImmutableList.of(t1Pk1.getColumnId()), null, t1Id);
|
||||
|
||||
Column t2Fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
ForeignKeyConstraint fk = new ForeignKeyConstraint(
|
||||
"fk_t2_t1",
|
||||
new ForeignKeyConstraint.TableIdentifier(t1.getId(), "test_db", t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2_fk2.getColumnId(), t1_pk1.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t1.getId(), MOCK_DB_NAME, t1.getName()),
|
||||
ImmutableList.of(Pair.create(t2Fk2.getColumnId(), t1Pk1.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2), ImmutableList.of(t2_fk2.getColumnId()), ImmutableList.of(fk));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2), ImmutableList.of(t2Fk2.getColumnId()),
|
||||
ImmutableList.of(fk), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2"));
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ColumnRefOperator t1_pk1_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2_ref = scanT2.getOutputVariables().get(0);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ColumnRefOperator t1Pk1Ref = scanT1ColMap.get(t1Pk1);
|
||||
ColumnRefOperator t2Fk2Ref = scanT2ColMap.get(t2Fk2);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2Ref, t1Pk1Ref);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2_ref, t1_pk1_ref);
|
||||
|
||||
// Test with INNER_JOIN
|
||||
LogicalJoinOperator innerJoinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.INNER_JOIN, onPredicate);
|
||||
OptExpression innerJoinExpr = OptExpression.create(innerJoinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
OptExpression innerJoinExpr = OptExpression.create(innerJoinOp, OptExpression.create(scanT2),
|
||||
OptExpression.create(scanT1));
|
||||
OptExpression transformedInnerExpr = applyRule(innerJoinExpr);
|
||||
assertEquals(JoinOperator.INNER_JOIN, ((LogicalJoinOperator) transformedInnerExpr.getOp()).getJoinType());
|
||||
|
||||
// Test with RIGHT_OUTER_JOIN
|
||||
LogicalJoinOperator rightOuterJoinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.RIGHT_OUTER_JOIN, onPredicate);
|
||||
OptExpression rightOuterJoinExpr = OptExpression.create(rightOuterJoinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
OptExpression rightOuterJoinExpr = OptExpression.create(rightOuterJoinOp, OptExpression.create(scanT2),
|
||||
OptExpression.create(scanT1));
|
||||
OptExpression transformedRightOuterExpr = applyRule(rightOuterJoinExpr);
|
||||
assertEquals(JoinOperator.RIGHT_OUTER_JOIN, ((LogicalJoinOperator) transformedRightOuterExpr.getOp()).getJoinType());
|
||||
assertEquals(JoinOperator.RIGHT_OUTER_JOIN,
|
||||
((LogicalJoinOperator) transformedRightOuterExpr.getOp()).getJoinType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFkToDifferentTable() {
|
||||
Column t1_pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1_pk1), ImmutableList.of(t1_pk1.getColumnId()), null);
|
||||
long t1Id = System.nanoTime();
|
||||
long t2Id = System.nanoTime();
|
||||
long t3Id = System.nanoTime();
|
||||
|
||||
Column t3_pk3 = createColumn("pk3", ScalarType.INT, false, true, "t3_c1"); // Another table
|
||||
OlapTable t3 = createMockTable("t3", ImmutableList.of(t3_pk3), ImmutableList.of(t3_pk3.getColumnId()), null);
|
||||
Column t1Pk1 = createColumn("pk1", ScalarType.INT, false, true, "t1_c1");
|
||||
OlapTable t1 = createMockTable("t1", ImmutableList.of(t1Pk1), ImmutableList.of(t1Pk1.getColumnId()), null, t1Id);
|
||||
|
||||
Column t3Pk3 = createColumn("pk3", ScalarType.INT, false, true, "t3_c1");
|
||||
OlapTable t3 = createMockTable("t3", ImmutableList.of(t3Pk3), ImmutableList.of(t3Pk3.getColumnId()), null, t3Id);
|
||||
|
||||
Column t2_fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
ForeignKeyConstraint fkToT3 = new ForeignKeyConstraint( // FK points to t3, not t1
|
||||
Column t2Fk2 = createColumn("fk2", ScalarType.INT, false, true, "t2_c1");
|
||||
ForeignKeyConstraint fkToT3 = new ForeignKeyConstraint(
|
||||
"fk_t2_t3",
|
||||
new ForeignKeyConstraint.TableIdentifier(t3.getId(), "test_db", t3.getName()),
|
||||
ImmutableList.of(Pair.create(t2_fk2.getColumnId(), t3_pk3.getColumnId()))
|
||||
new BaseTableInfo(MOCK_DB_ID, t3.getId(), MOCK_DB_NAME, t3.getName()),
|
||||
ImmutableList.of(Pair.create(t2Fk2.getColumnId(), t3Pk3.getColumnId()))
|
||||
);
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2_fk2), ImmutableList.of(t2_fk2.getColumnId()), ImmutableList.of(fkToT3));
|
||||
OlapTable t2 = createMockTable("t2", ImmutableList.of(t2Fk2), ImmutableList.of(t2Fk2.getColumnId()),
|
||||
ImmutableList.of(fkToT3), t2Id);
|
||||
|
||||
LogicalOlapScanOperator scanT1 = createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT2 = createScanOperator(t2, ImmutableList.of("fk2"));
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT1Info =
|
||||
createScanOperator(t1, ImmutableList.of("pk1"));
|
||||
LogicalOlapScanOperator scanT1 = scanT1Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT1ColMap = scanT1Info.second;
|
||||
|
||||
ColumnRefOperator t1_pk1_ref = scanT1.getOutputVariables().get(0);
|
||||
ColumnRefOperator t2_fk2_ref = scanT2.getOutputVariables().get(0);
|
||||
Pair<LogicalOlapScanOperator, Map<Column, ColumnRefOperator>> scanT2Info =
|
||||
createScanOperator(t2, ImmutableList.of("fk2"));
|
||||
LogicalOlapScanOperator scanT2 = scanT2Info.first;
|
||||
Map<Column, ColumnRefOperator> scanT2ColMap = scanT2Info.second;
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryPredicateOperator.BinaryType.EQ, t2_fk2_ref, t1_pk1_ref);
|
||||
ColumnRefOperator t1Pk1Ref = scanT1ColMap.get(t1Pk1);
|
||||
ColumnRefOperator t2Fk2Ref = scanT2ColMap.get(t2Fk2);
|
||||
|
||||
ScalarOperator onPredicate = new BinaryPredicateOperator(BinaryType.EQ, t2Fk2Ref, t1Pk1Ref);
|
||||
LogicalJoinOperator joinOp = createJoinOperator(OptExpression.create(scanT2), OptExpression.create(scanT1),
|
||||
JoinOperator.LEFT_OUTER_JOIN, onPredicate);
|
||||
OptExpression joinExpr = OptExpression.create(joinOp, OptExpression.create(scanT2), OptExpression.create(scanT1));
|
||||
|
|
|
|||
Loading…
Reference in New Issue