Skip to content

Commit 53f3132

Browse files
committed
[CALCITE-7442] Correlated variable has wrong index inside subquery
1 parent af6367d commit 53f3132

File tree

3 files changed

+162
-13
lines changed

3 files changed

+162
-13
lines changed

core/src/main/java/org/apache/calcite/plan/RelOptUtil.java

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,7 +2957,8 @@ public static boolean classifyFilters(
29572957
joinFields,
29582958
nTotalFields,
29592959
leftFields,
2960-
filter);
2960+
filter,
2961+
joinRel.getInput(0));
29612962

29622963
leftFilters.add(shiftedFilter);
29632964
}
@@ -2975,7 +2976,8 @@ public static boolean classifyFilters(
29752976
joinFields,
29762977
nTotalFields,
29772978
rightFields,
2978-
filter);
2979+
filter,
2980+
joinRel.getInput(1));
29792981
rightFilters.add(shiftedFilter);
29802982
}
29812983
filtersToRemove.add(filter);
@@ -3079,7 +3081,8 @@ public static boolean classifyFilters(
30793081
joinFields,
30803082
nTotalFields,
30813083
leftFields,
3082-
filter);
3084+
filter,
3085+
joinRel.getInput(0));
30833086

30843087
leftFilters.add(shiftedFilter);
30853088
}
@@ -3105,7 +3108,8 @@ public static boolean classifyFilters(
31053108
joinFields,
31063109
nTotalFields,
31073110
rightFields,
3108-
filter);
3111+
filter,
3112+
joinRel.getInput(1));
31093113
rightFilters.add(shiftedFilter);
31103114
}
31113115
filtersToRemove.add(filter);
@@ -3140,7 +3144,8 @@ private static RexNode shiftFilter(
31403144
List<RelDataTypeField> joinFields,
31413145
int nTotalFields,
31423146
List<RelDataTypeField> rightFields,
3143-
RexNode filter) {
3147+
RexNode filter,
3148+
RelNode child) {
31443149
int[] adjustments = new int[nTotalFields];
31453150
for (int i = start; i < end; i++) {
31463151
adjustments[i] = offset;
@@ -3150,7 +3155,9 @@ private static RexNode shiftFilter(
31503155
rexBuilder,
31513156
joinFields,
31523157
rightFields,
3153-
adjustments));
3158+
adjustments,
3159+
offset,
3160+
child));
31543161
}
31553162

31563163
/**
@@ -4766,6 +4773,8 @@ public static class RexInputConverter extends RexShuttle {
47664773
private final @Nullable List<RelDataTypeField> rightDestFields;
47674774
private final int nLeftDestFields;
47684775
private final int[] adjustments;
4776+
private final int offset;
4777+
private final RelNode correlateVariableChild;
47694778

47704779
/**
47714780
* Creates a RexInputConverter.
@@ -4791,7 +4800,9 @@ private RexInputConverter(
47914800
@Nullable List<RelDataTypeField> destFields,
47924801
@Nullable List<RelDataTypeField> leftDestFields,
47934802
@Nullable List<RelDataTypeField> rightDestFields,
4794-
int[] adjustments) {
4803+
int[] adjustments,
4804+
int offset,
4805+
RelNode correlateVariableChild) {
47954806
this.rexBuilder = rexBuilder;
47964807
this.srcFields = srcFields;
47974808
this.destFields = destFields;
@@ -4804,6 +4815,8 @@ private RexInputConverter(
48044815
assert destFields == null;
48054816
nLeftDestFields = leftDestFields.size();
48064817
}
4818+
this.offset = offset;
4819+
this.correlateVariableChild = correlateVariableChild;
48074820
}
48084821

48094822
public RexInputConverter(
@@ -4818,22 +4831,55 @@ public RexInputConverter(
48184831
null,
48194832
leftDestFields,
48204833
rightDestFields,
4821-
adjustments);
4834+
adjustments,
4835+
0,
4836+
null);
48224837
}
48234838

48244839
public RexInputConverter(
48254840
RexBuilder rexBuilder,
48264841
@Nullable List<RelDataTypeField> srcFields,
48274842
@Nullable List<RelDataTypeField> destFields,
48284843
int[] adjustments) {
4829-
this(rexBuilder, srcFields, destFields, null, null, adjustments);
4844+
this(rexBuilder, srcFields, destFields, null, null, adjustments, 0, null);
48304845
}
48314846

48324847
public RexInputConverter(
48334848
RexBuilder rexBuilder,
48344849
@Nullable List<RelDataTypeField> srcFields,
48354850
int[] adjustments) {
4836-
this(rexBuilder, srcFields, null, null, null, adjustments);
4851+
this(rexBuilder, srcFields, null, null, null, adjustments, 0, null);
4852+
}
4853+
4854+
public RexInputConverter(
4855+
RexBuilder rexBuilder,
4856+
@Nullable List<RelDataTypeField> srcFields,
4857+
@Nullable List<RelDataTypeField> destFields,
4858+
int[] adjustments,
4859+
int offset,
4860+
RelNode child) {
4861+
this(rexBuilder, srcFields, destFields, null, null, adjustments, offset, child);
4862+
}
4863+
4864+
@Override public RexNode visitSubQuery(RexSubQuery subQuery) {
4865+
boolean[] update = {false};
4866+
List<RexNode> clonedOperands = visitList(subQuery.operands, update);
4867+
if (update[0]) {
4868+
subQuery = subQuery.clone(subQuery.getType(), clonedOperands);
4869+
final Set<CorrelationId> variablesSet = RelOptUtil.getVariablesUsed(subQuery.rel);
4870+
if (!variablesSet.isEmpty() && correlateVariableChild != null) {
4871+
CorrelationId id = Iterables.getOnlyElement(variablesSet);
4872+
RelNode newSubQueryRel = subQuery.rel.accept(new RelHomogeneousShuttle() {
4873+
@Override public RelNode visit(RelNode other) {
4874+
RelNode node =
4875+
RexUtil.shiftFieldAccess(rexBuilder, other, id, correlateVariableChild, offset);
4876+
return super.visit(node);
4877+
}
4878+
});
4879+
subQuery = subQuery.clone(newSubQueryRel);
4880+
}
4881+
}
4882+
return subQuery;
48374883
}
48384884

48394885
@Override public RexNode visitInputRef(RexInputRef var) {

core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.apache.calcite.plan.RelOptUtil;
2121
import org.apache.calcite.plan.RelRule;
2222
import org.apache.calcite.rel.RelNode;
23+
import org.apache.calcite.rel.core.CorrelationId;
2324
import org.apache.calcite.rel.core.Filter;
2425
import org.apache.calcite.rel.core.Join;
2526
import org.apache.calcite.rel.core.JoinRelType;
@@ -29,6 +30,7 @@
2930
import org.apache.calcite.rex.RexCall;
3031
import org.apache.calcite.rex.RexInputRef;
3132
import org.apache.calcite.rex.RexNode;
33+
import org.apache.calcite.rex.RexSubQuery;
3234
import org.apache.calcite.rex.RexUtil;
3335
import org.apache.calcite.sql.SqlKind;
3436
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -198,14 +200,39 @@ protected void perform(RelOptRuleCall call, @Nullable Filter filter,
198200
return;
199201
}
200202

203+
Set<CorrelationId> variablesSet;
204+
Set<CorrelationId> leftVariablesSet = new LinkedHashSet<>();
205+
Set<CorrelationId> rightVariablesSet = new LinkedHashSet<>();
206+
if (filter == null) {
207+
variablesSet = ImmutableSet.of();
208+
} else {
209+
variablesSet = new LinkedHashSet<>(filter.getVariablesSet());
210+
for (RexNode condition : leftFilters) {
211+
RexSubQuery rexSubQuery = RexUtil.SubQueryFinder.find(condition);
212+
if (rexSubQuery != null) {
213+
leftVariablesSet.addAll(RelOptUtil.getVariablesUsed(rexSubQuery.rel));
214+
}
215+
}
216+
217+
for (RexNode condition : rightFilters) {
218+
RexSubQuery rexSubQuery = RexUtil.SubQueryFinder.find(condition);
219+
if (rexSubQuery != null) {
220+
rightVariablesSet.addAll(RelOptUtil.getVariablesUsed(rexSubQuery.rel));
221+
}
222+
}
223+
224+
variablesSet.addAll(leftVariablesSet);
225+
variablesSet.addAll(rightVariablesSet);
226+
}
227+
201228
// create Filters on top of the children if any filters were
202229
// pushed to them
203230
final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
204231
final RelBuilder relBuilder = call.builder();
205232
final RelNode leftRel =
206-
relBuilder.push(join.getLeft()).filter(leftFilters).build();
233+
relBuilder.push(join.getLeft()).filter(leftVariablesSet, leftFilters).build();
207234
final RelNode rightRel =
208-
relBuilder.push(join.getRight()).filter(rightFilters).build();
235+
relBuilder.push(join.getRight()).filter(rightVariablesSet, rightFilters).build();
209236

210237
// create the new join node referencing the new children and
211238
// containing its new join filters (if there are any)
@@ -250,7 +277,7 @@ protected void perform(RelOptRuleCall call, @Nullable Filter filter,
250277

251278
// create a FilterRel on top of the join if needed
252279
relBuilder.filter(
253-
filter == null ? ImmutableSet.of() : filter.getVariablesSet(),
280+
variablesSet,
254281
RexUtil.fixUp(rexBuilder, aboveFilters,
255282
RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType())));
256283
call.transformTo(relBuilder.build());

core/src/test/java/org/apache/calcite/sql2rel/RelDecorrelatorTest.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,4 +1830,80 @@ public static Frameworks.ConfigBuilder config() {
18301830
+ " LogicalTableScan(table=[[scott, EMP]])\n";
18311831
assertThat(after, hasTree(planAfter));
18321832
}
1833+
1834+
/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-7442">[CALCITE-7442]
1835+
* Getting Wrong index of Correlated variable inside Subquery after FilterJoinRule</a>. */
1836+
@Test void testCorrelatedVariableIndex() {
1837+
final FrameworkConfig frameworkConfig = config().build();
1838+
final RelBuilder builder = RelBuilder.create(frameworkConfig);
1839+
final RelOptCluster cluster = builder.getCluster();
1840+
final Planner planner = Frameworks.getPlanner(frameworkConfig);
1841+
final String sql = "select e.empno, d.dname, b.ename\n"
1842+
+ "from emp e\n"
1843+
+ "inner join dept d\n"
1844+
+ " on d.deptno = e.deptno\n"
1845+
+ "inner join bonus b\n"
1846+
+ " on e.ename = b.ename\n"
1847+
+ " and b.job in (\n"
1848+
+ " select b2.job\n"
1849+
+ " from bonus b2\n"
1850+
+ " where b2.ename = b.ename)\n"
1851+
+ "where e.sal > 1000 and d.dname = 'SALES'";
1852+
1853+
final RelNode originalRel;
1854+
try {
1855+
final SqlNode parse = planner.parse(sql);
1856+
final SqlNode validate = planner.validate(parse);
1857+
originalRel = planner.rel(validate).rel;
1858+
} catch (Exception e) {
1859+
throw TestUtil.rethrow(e);
1860+
}
1861+
1862+
final HepProgram hepProgram = HepProgram.builder()
1863+
.addRuleCollection(
1864+
ImmutableList.of(
1865+
CoreRules.FILTER_INTO_JOIN,
1866+
CoreRules.FILTER_SUB_QUERY_TO_CORRELATE))
1867+
.build();
1868+
final Program program =
1869+
Programs.of(hepProgram, true,
1870+
requireNonNull(cluster.getMetadataProvider()));
1871+
final RelNode before =
1872+
program.run(cluster.getPlanner(), originalRel, cluster.traitSet(),
1873+
Collections.emptyList(), Collections.emptyList());
1874+
1875+
final String planBefore = "LogicalProject(EMPNO=[$0], DNAME=[$9], ENAME=[$11])\n"
1876+
+ " LogicalJoin(condition=[=($1, $11)], joinType=[inner], variablesSet=[[$cor0]])\n"
1877+
+ " LogicalJoin(condition=[=($8, $7)], joinType=[inner])\n"
1878+
+ " LogicalFilter(condition=[>(CAST($5):DECIMAL(12, 2), 1000.00)])\n"
1879+
+ " LogicalTableScan(table=[[scott, EMP]])\n"
1880+
+ " LogicalFilter(condition=[=($1, 'SALES')])\n"
1881+
+ " LogicalTableScan(table=[[scott, DEPT]])\n"
1882+
+ " LogicalProject(ENAME=[$0], JOB=[$1], SAL=[$2], COMM=[$3])\n"
1883+
+ " LogicalFilter(condition=[=($1, $4)])\n"
1884+
+ " LogicalCorrelate(correlation=[$cor0], joinType=[inner], requiredColumns=[{0}])\n"
1885+
+ " LogicalTableScan(table=[[scott, BONUS]])\n"
1886+
+ " LogicalProject(JOB=[$1])\n"
1887+
+ " LogicalFilter(condition=[=($0, $cor0.ENAME)])\n"
1888+
+ " LogicalTableScan(table=[[scott, BONUS]])\n";
1889+
assertThat(before, hasTree(planBefore));
1890+
1891+
final RelNode after =
1892+
RelDecorrelator.decorrelateQuery(before, builder, RuleSets.ofList(Collections.emptyList()),
1893+
RuleSets.ofList(Collections.emptyList()));
1894+
final String planAfter = "LogicalProject(EMPNO=[$0], DNAME=[$9], ENAME=[$11])\n"
1895+
+ " LogicalJoin(condition=[=($1, $11)], joinType=[inner])\n"
1896+
+ " LogicalJoin(condition=[=($8, $7)], joinType=[inner])\n"
1897+
+ " LogicalFilter(condition=[>(CAST($5):DECIMAL(12, 2), 1000.00)])\n"
1898+
+ " LogicalTableScan(table=[[scott, EMP]])\n"
1899+
+ " LogicalFilter(condition=[=($1, 'SALES')])\n"
1900+
+ " LogicalTableScan(table=[[scott, DEPT]])\n"
1901+
+ " LogicalProject(ENAME=[$0], JOB=[$1], SAL=[$2], COMM=[$3])\n"
1902+
+ " LogicalJoin(condition=[AND(=($0, $5), =($1, $4))], joinType=[inner])\n"
1903+
+ " LogicalTableScan(table=[[scott, BONUS]])\n"
1904+
+ " LogicalProject(JOB=[$1], ENAME=[$0])\n"
1905+
+ " LogicalFilter(condition=[IS NOT NULL($0)])\n"
1906+
+ " LogicalTableScan(table=[[scott, BONUS]])\n";
1907+
assertThat(after, hasTree(planAfter));
1908+
}
18331909
}

0 commit comments

Comments
 (0)