You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2014/12/27 09:04:06 UTC
[2/2] incubator-calcite git commit: [CALCITE-448] FilterIntoJoinRule
creates filters containing invalid RexInputRef
[CALCITE-448] FilterIntoJoinRule creates filters containing invalid RexInputRef
Refactor FilterJoinRule so you can supply a predicate rather than sub-classing.
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/011a6b4c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/011a6b4c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/011a6b4c
Branch: refs/heads/master
Commit: 011a6b4ce6cc999bab126dfc2fee58c23aaab074
Parents: 3a225ed
Author: julianhyde <jh...@apache.org>
Authored: Fri Dec 26 22:04:28 2014 -0800
Committer: julianhyde <jh...@apache.org>
Committed: Fri Dec 26 23:10:19 2014 -0800
----------------------------------------------------------------------
.../org/apache/calcite/rel/core/Filter.java | 7 +-
.../calcite/rel/rules/FilterJoinRule.java | 105 +++++++++++--------
.../rel/rules/FilterProjectTransposeRule.java | 31 ++++--
.../apache/calcite/rex/RexProgramBuilder.java | 2 +-
.../java/org/apache/calcite/rex/RexUtil.java | 45 +++++++-
.../apache/calcite/test/RelOptRulesTest.java | 43 ++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 36 +++++++
7 files changed, 213 insertions(+), 56 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/main/java/org/apache/calcite/rel/core/Filter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Filter.java b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
index ca271db..0dc3009 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Filter.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Filter.java
@@ -20,6 +20,7 @@ import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.prepare.CalcitePrepareImpl;
import org.apache.calcite.rel.RelInput;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
@@ -72,7 +73,7 @@ public abstract class Filter extends SingleRel {
assert RexUtil.isFlat(condition) : condition;
this.condition = condition;
// Too expensive for everyday use:
- // assert isValid(true);
+ assert !CalcitePrepareImpl.DEBUG || isValid(true);
}
/**
@@ -110,6 +111,10 @@ public abstract class Filter extends SingleRel {
}
@Override public boolean isValid(boolean fail) {
+ if (RexUtil.isNullabilityCast(getCluster().getTypeFactory(), condition)) {
+ assert !fail : "Cast for just nullability not allowed";
+ return false;
+ }
final RexChecker checker = new RexChecker(getInput().getRowType(), fail);
condition.accept(checker);
if (checker.getFailureCount() > 0) {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
index bef2844..bb0b8cc 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java
@@ -35,6 +35,7 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -42,18 +43,31 @@ import java.util.List;
* within a join node into the join node and/or its children nodes.
*/
public abstract class FilterJoinRule extends RelOptRule {
+ /** Predicate that always returns true. With this predicate, every filter
+ * will be pushed into the ON clause. */
+ public static final Predicate TRUE_PREDICATE =
+ new Predicate() {
+ public boolean apply(Join join, JoinRelType joinType, RexNode exp) {
+ return true;
+ }
+ };
+
+ /** Rule that pushes predicates from a Filter into the Join below them. */
public static final FilterJoinRule FILTER_ON_JOIN =
- new FilterIntoJoinRule(true);
+ new FilterIntoJoinRule(true, RelFactories.DEFAULT_FILTER_FACTORY,
+ RelFactories.DEFAULT_PROJECT_FACTORY, TRUE_PREDICATE);
/** Dumber version of {@link #FILTER_ON_JOIN}. Not intended for production
* use, but keeps some tests working for which {@code FILTER_ON_JOIN} is too
* smart. */
public static final FilterJoinRule DUMB_FILTER_ON_JOIN =
- new FilterIntoJoinRule(false);
+ new FilterIntoJoinRule(false, RelFactories.DEFAULT_FILTER_FACTORY,
+ RelFactories.DEFAULT_PROJECT_FACTORY, TRUE_PREDICATE);
+ /** Rule that pushes predicates in a Join into the inputs to the join. */
public static final FilterJoinRule JOIN =
new JoinConditionPushRule(RelFactories.DEFAULT_FILTER_FACTORY,
- RelFactories.DEFAULT_PROJECT_FACTORY);
+ RelFactories.DEFAULT_PROJECT_FACTORY, TRUE_PREDICATE);
/** Whether to try to strengthen join-type. */
private final boolean smart;
@@ -62,6 +76,11 @@ public abstract class FilterJoinRule extends RelOptRule {
private final RelFactories.ProjectFactory projectFactory;
+ /** Predicate that returns whether a filter is valid in the ON clause of a
+ * join for this particular kind of join. If not, Calcite will push it back to
+ * above the join. */
+ private final Predicate predicate;
+
//~ Constructors -----------------------------------------------------------
/**
@@ -71,10 +90,22 @@ public abstract class FilterJoinRule extends RelOptRule {
protected FilterJoinRule(RelOptRuleOperand operand, String id,
boolean smart, RelFactories.FilterFactory filterFactory,
RelFactories.ProjectFactory projectFactory) {
- super(operand, "PushFilterRule:" + id);
+ this(operand, id, smart, filterFactory, projectFactory, TRUE_PREDICATE);
+ }
+
+ /**
+ * Creates a FilterProjectTransposeRule with an explicit root operand and
+ * factories.
+ */
+ protected FilterJoinRule(RelOptRuleOperand operand, String id,
+ boolean smart, RelFactories.FilterFactory filterFactory,
+ RelFactories.ProjectFactory projectFactory,
+ Predicate predicate) {
+ super(operand, "FilterJoinRule:" + id);
this.smart = smart;
this.filterFactory = filterFactory;
this.projectFactory = projectFactory;
+ this.predicate = predicate;
}
//~ Methods ----------------------------------------------------------------
@@ -218,7 +249,9 @@ public abstract class FilterJoinRule extends RelOptRule {
// create a LogicalFilter on top of the join if needed
RelNode newRel =
- RelOptUtil.createFilter(newJoinRel, aboveFilters, filterFactory);
+ RelOptUtil.createFilter(newJoinRel,
+ RexUtil.fixUp(rexBuilder, aboveFilters, newJoinRel.getRowType()),
+ filterFactory);
call.transformTo(newRel);
}
@@ -236,45 +269,28 @@ public abstract class FilterJoinRule extends RelOptRule {
* @param aboveFilters Filter above Join
* @param joinFilters Filters in join condition
* @param join Join
- *
- * @deprecated Use
- * {@link #validateJoinFilters(java.util.List, java.util.List, org.apache.calcite.rel.core.Join, org.apache.calcite.rel.core.JoinRelType)};
- * very short-term; will be removed before
- * {@link org.apache.calcite.util.Bug#upgrade(String) calcite-0.9.2}.
- */
- protected void validateJoinFilters(List<RexNode> aboveFilters,
- List<RexNode> joinFilters, Join join) {
- validateJoinFilters(aboveFilters, joinFilters, join, join.getJoinType());
- }
-
- /**
- * Validates that target execution framework can satisfy join filters.
- *
- * <p>If the join filter cannot be satisfied (for example, if it is
- * {@code l.c1 > r.c2} and the join only supports equi-join), removes the
- * filter from {@code joinFilters} and adds it to {@code aboveFilters}.
- *
- * <p>The default implementation does nothing; i.e. the join can handle all
- * conditions.
- *
- * @param aboveFilters Filter above Join
- * @param joinFilters Filters in join condition
- * @param join Join
* @param joinType JoinRelType could be different from type in Join due to
* outer join simplification.
*/
protected void validateJoinFilters(List<RexNode> aboveFilters,
List<RexNode> joinFilters, Join join, JoinRelType joinType) {
- return;
+ final Iterator<RexNode> filterIter = joinFilters.iterator();
+ while (filterIter.hasNext()) {
+ RexNode exp = filterIter.next();
+ if (!predicate.apply(join, joinType, exp)) {
+ aboveFilters.add(exp);
+ filterIter.remove();
+ }
+ }
}
/** Rule that pushes parts of the join condition to its inputs. */
public static class JoinConditionPushRule extends FilterJoinRule {
public JoinConditionPushRule(RelFactories.FilterFactory filterFactory,
- RelFactories.ProjectFactory projectFactory) {
+ RelFactories.ProjectFactory projectFactory, Predicate predicate) {
super(RelOptRule.operand(Join.class, RelOptRule.any()),
- "FilterJoinRule:no-filter",
- true, filterFactory, projectFactory);
+ "FilterJoinRule:no-filter", true, filterFactory, projectFactory,
+ predicate);
}
@Override public void onMatch(RelOptRuleCall call) {
@@ -286,19 +302,15 @@ public abstract class FilterJoinRule extends RelOptRule {
/** Rule that tries to push filter expressions into a join
* condition and into the inputs of the join. */
public static class FilterIntoJoinRule extends FilterJoinRule {
- public FilterIntoJoinRule(boolean smart) {
- this(smart, RelFactories.DEFAULT_FILTER_FACTORY,
- RelFactories.DEFAULT_PROJECT_FACTORY);
- }
-
public FilterIntoJoinRule(boolean smart,
RelFactories.FilterFactory filterFactory,
- RelFactories.ProjectFactory projectFactory) {
+ RelFactories.ProjectFactory projectFactory,
+ Predicate predicate) {
super(
- RelOptRule.operand(Filter.class,
- RelOptRule.operand(Join.class, RelOptRule.any())),
- "FilterJoinRule:filter",
- smart, filterFactory, projectFactory);
+ operand(Filter.class,
+ operand(Join.class, RelOptRule.any())),
+ "FilterJoinRule:filter", smart, filterFactory, projectFactory,
+ predicate);
}
@Override public void onMatch(RelOptRuleCall call) {
@@ -307,6 +319,13 @@ public abstract class FilterJoinRule extends RelOptRule {
perform(call, filter, join);
}
}
+
+ /** Predicate that returns whether a filter is valid in the ON clause of a
+ * join for this particular kind of join. If not, Calcite will push it back to
+ * above the join. */
+ public interface Predicate {
+ boolean apply(Join join, JoinRelType joinType, RexNode exp);
+ }
}
// End FilterJoinRule.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
index 2f5b720..802cf87 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java
@@ -23,8 +23,11 @@ import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexOver;
+import org.apache.calcite.rex.RexUtil;
/**
* Planner rule that pushes
@@ -70,10 +73,10 @@ public class FilterProjectTransposeRule extends RelOptRule {
// implement RelOptRule
public void onMatch(RelOptRuleCall call) {
- final Filter filterRel = call.rel(0);
- final Project projRel = call.rel(1);
+ final Filter filter = call.rel(0);
+ final Project project = call.rel(1);
- if (RexOver.containsOver(projRel.getProjects(), null)) {
+ if (RexOver.containsOver(project.getProjects(), null)) {
// In general a filter cannot be pushed below a windowing calculation.
// Applying the filter before the aggregation function changes
// the results of the windowing invocation.
@@ -85,20 +88,28 @@ public class FilterProjectTransposeRule extends RelOptRule {
// convert the filter to one that references the child of the project
RexNode newCondition =
- RelOptUtil.pushFilterPastProject(filterRel.getCondition(), projRel);
+ RelOptUtil.pushFilterPastProject(filter.getCondition(), project);
+
+ // Remove cast of BOOLEAN NOT NULL to BOOLEAN or vice versa. Filter accepts
+ // nullable and not-nullable conditions, but a CAST might get in the way of
+ // other rewrites.
+ final RelDataTypeFactory typeFactory = filter.getCluster().getTypeFactory();
+ if (RexUtil.isNullabilityCast(typeFactory, newCondition)) {
+ newCondition = ((RexCall) newCondition).getOperands().get(0);
+ }
RelNode newFilterRel =
filterFactory == null
- ? filterRel.copy(filterRel.getTraitSet(), projRel.getInput(),
+ ? filter.copy(filter.getTraitSet(), project.getInput(),
newCondition)
- : filterFactory.createFilter(projRel.getInput(), newCondition);
+ : filterFactory.createFilter(project.getInput(), newCondition);
RelNode newProjRel =
projectFactory == null
- ? projRel.copy(projRel.getTraitSet(), newFilterRel,
- projRel.getProjects(), projRel.getRowType())
- : projectFactory.createProject(newFilterRel, projRel.getProjects(),
- projRel.getRowType().getFieldNames());
+ ? project.copy(project.getTraitSet(), newFilterRel,
+ project.getProjects(), project.getRowType())
+ : projectFactory.createProject(newFilterRel, project.getProjects(),
+ project.getRowType().getFieldNames());
call.transformTo(newProjRel);
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
index 2b70160..f0d5c3d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
@@ -36,7 +36,7 @@ import java.util.Map;
*
* <p>RexProgramBuilder is necessary because a {@link RexProgram} is immutable.
* (The {@link String} class has the same problem: it is immutable, so they
- * introduced {@link StringBuffer}.)
+ * introduced {@link StringBuilder}.)
*/
public class RexProgramBuilder {
//~ Instance fields --------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 8fa85c0..c5efd29 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -204,7 +204,21 @@ public class RexUtil {
}
}
- /**
+ /** Returns whether an expression is a cast just for the purposes of
+ * nullability, not changing any other aspect of the type. */
+ public static boolean isNullabilityCast(RelDataTypeFactory typeFactory,
+ RexNode node) {
+ switch (node.getKind()) {
+ case CAST:
+ final RexCall call = (RexCall) node;
+ final RexNode arg0 = call.getOperands().get(0);
+ return SqlTypeUtil.equalSansNullability(typeFactory, arg0.getType(),
+ call.getType());
+ }
+ return false;
+ }
+
+ /**
* Returns whether a given node contains a RexCall with a specified operator
*
* @param operator to look for
@@ -1075,6 +1089,35 @@ public class RexUtil {
return new CnfHelper(rexBuilder).pull(node);
}
+ /** Fixes up the type of all {@link RexInputRef}s in an
+ * expression to match differences in nullability.
+ *
+ * <p>Such differences in nullability occur when expressions are moved
+ * through outer joins.
+ *
+ * <p>Throws if there any greater inconsistencies of type. */
+ public static List<RexNode> fixUp(final RexBuilder rexBuilder,
+ List<RexNode> nodes, final RelDataType rowType) {
+ final List<RelDataType> typeList = RelOptUtil.getFieldTypeList(rowType);
+ return new RexShuttle() {
+ @Override public RexNode visitInputRef(RexInputRef ref) {
+ final RelDataType rightType = typeList.get(ref.getIndex());
+ final RelDataType refType = ref.getType();
+ if (refType == rightType) {
+ return ref;
+ }
+ final RelDataType refType2 =
+ rexBuilder.getTypeFactory().createTypeWithNullability(refType,
+ rightType.isNullable());
+ if (refType2 == rightType) {
+ return new RexInputRef(ref.getIndex(), refType2);
+ }
+ throw new AssertionError("mismatched type " + ref + " " + rightType);
+ }
+ // CHECKSTYLE: IGNORE 1
+ }.apply(nodes);
+ }
+
//~ Inner Classes ----------------------------------------------------------
/**
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index cd2afe4..4e50e89 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -24,6 +24,9 @@ import org.apache.calcite.plan.hep.HepProgram;
import org.apache.calcite.plan.hep.HepProgramBuilder;
import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.logical.LogicalTableModify;
import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
@@ -66,6 +69,7 @@ import org.apache.calcite.rel.rules.UnionToDistinctRule;
import org.apache.calcite.rel.rules.ValuesReduceRule;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlValidator;
@@ -242,6 +246,45 @@ public class RelOptRulesTest extends RelOptTestBase {
+ "where dept1.c1 > 'c' and (dept1.c2 > 30 or dept1.c1 < 'z')");
}
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-448">[CALCITE-448],
+ * FilterIntoJoinRule creates filters containing invalid RexInputRef</a>. */
+ @Test public void testPushFilterPastProject() {
+ final HepProgram preProgram =
+ HepProgram.builder()
+ .addRuleInstance(ProjectMergeRule.INSTANCE)
+ .build();
+ final FilterJoinRule.Predicate predicate =
+ new FilterJoinRule.Predicate() {
+ public boolean apply(Join join, JoinRelType joinType, RexNode exp) {
+ return joinType != JoinRelType.INNER;
+ }
+ };
+ final FilterJoinRule join =
+ new FilterJoinRule.JoinConditionPushRule(
+ RelFactories.DEFAULT_FILTER_FACTORY,
+ RelFactories.DEFAULT_PROJECT_FACTORY, predicate);
+ final FilterJoinRule filterOnJoin =
+ new FilterJoinRule.FilterIntoJoinRule(true,
+ RelFactories.DEFAULT_FILTER_FACTORY,
+ RelFactories.DEFAULT_PROJECT_FACTORY, predicate);
+ final HepProgram program =
+ HepProgram.builder()
+ .addGroupBegin()
+ .addRuleInstance(FilterProjectTransposeRule.INSTANCE)
+ .addRuleInstance(join)
+ .addRuleInstance(filterOnJoin)
+ .addGroupEnd()
+ .build();
+ checkPlanning(tester,
+ preProgram,
+ new HepPlanner(program),
+ "select a.name\n"
+ + "from dept a\n"
+ + "left join dept b on b.deptno > 10\n"
+ + "right join dept c on b.deptno > 10\n");
+ }
+
@Test public void testSemiJoinRule() {
final HepProgram preProgram =
HepProgram.builder()
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/011a6b4c/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 436e0b2..8603a5f 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -3167,6 +3167,42 @@ LogicalProject(C1=[$0])
]]>
</Resource>
</TestCase>
+ <TestCase name="testPushFilterPastProject">
+ <Resource name="sql">
+ <![CDATA[select a.name
+from dept a
+left join dept b on b.deptno > 10
+right join dept c on b.deptno > 10
+]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(NAME=[$1])
+ LogicalJoin(condition=[$4], joinType=[right])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], DEPTNO0=[$2], NAME0=[$3], $f4=[>($2, 10)])
+ LogicalJoin(condition=[$4], joinType=[left])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[>($0, 10)])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(NAME=[$1])
+ LogicalJoin(condition=[true], joinType=[right])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], DEPTNO0=[$2], NAME0=[$3], $f4=[>($2, 10)])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], DEPTNO0=[CAST($2):INTEGER], NAME0=[CAST($3):VARCHAR(10) CHARACTER SET "ISO-8859-1" COLLATE "ISO-8859-1$en_US$primary"], $f2=[CAST($4):BOOLEAN])
+ LogicalJoin(condition=[true], joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalProject(DEPTNO=[$0], NAME=[$1], $f2=[>($0, 10)])
+ LogicalFilter(condition=[>($0, 10)])
+ LogicalFilter(condition=[>($0, 10)])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+ LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+ </Resource>
+ </TestCase>
<TestCase name="testMergeFilter">
<Resource name="sql">
<![CDATA[select name from (