Skip to content

Commit 92cfb30

Browse files
committed
[CALCITE-5733] Simplify "a = ARRAY[1,2] AND a = ARRAY[2,3]" to "false"
1 parent 3d14332 commit 92cfb30

File tree

3 files changed

+239
-14
lines changed

3 files changed

+239
-14
lines changed

core/src/main/java/org/apache/calcite/rex/RexAnalyzer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ private static List<Comparable> getComparables(RexNode variable) {
9696
values.add(0); // 00:00:00.000
9797
values.add(86_399_000); // 23:59:59.000
9898
break;
99+
case ARRAY:
100+
case MAP:
101+
case MULTISET:
102+
break;
99103
default:
100104
throw new AssertionError("don't know values for " + variable
101105
+ " of type " + variable.getType());

core/src/main/java/org/apache/calcite/rex/RexSimplify.java

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import com.google.common.collect.ArrayListMultimap;
4444
import com.google.common.collect.BoundType;
45+
import com.google.common.collect.HashMultiset;
4546
import com.google.common.collect.ImmutableList;
4647
import com.google.common.collect.ImmutableRangeSet;
4748
import com.google.common.collect.ImmutableSet;
@@ -65,6 +66,7 @@
6566
import java.util.LinkedHashSet;
6667
import java.util.List;
6768
import java.util.Map;
69+
import java.util.Objects;
6870
import java.util.Set;
6971
import java.util.regex.Matcher;
7072
import java.util.regex.Pattern;
@@ -1853,7 +1855,7 @@ private <C extends Comparable<C>> RexNode simplifyAnd2ForUnknownAsFalse(
18531855
ArrayListMultimap.create();
18541856
final Map<RexNode, Pair<Range<C>, List<RexNode>>> rangeTerms =
18551857
new HashMap<>();
1856-
final Map<RexNode, RexLiteral> equalityConstantTerms = new HashMap<>();
1858+
final Map<RexNode, RexNode> equalityConstantTerms = new HashMap<>();
18571859
final Set<RexNode> negatedTerms = new HashSet<>();
18581860
final Set<RexNode> nullOperands = new HashSet<>();
18591861
final Set<RexNode> notNullOperands = new LinkedHashSet<>();
@@ -1915,14 +1917,15 @@ private <C extends Comparable<C>> RexNode simplifyAnd2ForUnknownAsFalse(
19151917
// is equal to different constants, this condition cannot be satisfied,
19161918
// and hence it can be evaluated to FALSE
19171919
if (term.getKind() == SqlKind.EQUALS) {
1918-
if (comparison != null) {
1919-
final RexLiteral literal = comparison.literal;
1920-
final RexLiteral prevLiteral =
1921-
equalityConstantTerms.put(comparison.ref, literal);
1922-
1923-
if (prevLiteral != null
1924-
&& literal.getType().equals(prevLiteral.getType())
1925-
&& !literal.equals(prevLiteral)) {
1920+
final Pair<RexNode, RexNode> constantEquality = constantEquality(call);
1921+
if (constantEquality != null) {
1922+
final RexNode constant = constantEquality.right;
1923+
final RexNode prevConstant =
1924+
equalityConstantTerms.put(constantEquality.left, constant);
1925+
1926+
if (prevConstant != null
1927+
&& constant.getType().equals(prevConstant.getType())
1928+
&& !constantsEquivalent(constant, prevConstant)) {
19261929
return rexBuilder.makeLiteral(false);
19271930
}
19281931
} else if (RexUtil.isReferenceOrAccess(left, true)
@@ -1983,17 +1986,18 @@ private <C extends Comparable<C>> RexNode simplifyAnd2ForUnknownAsFalse(
19831986
// Example #1. x=5 AND y=5 AND x=y : x=5 AND y=5
19841987
// Example #2. x=5 AND y=6 AND x=y - not satisfiable
19851988
for (RexNode ref1 : equalityTerms.keySet()) {
1986-
final RexLiteral literal1 = equalityConstantTerms.get(ref1);
1987-
if (literal1 == null) {
1989+
final RexNode constant1 = equalityConstantTerms.get(ref1);
1990+
if (constant1 == null) {
19881991
continue;
19891992
}
19901993
Collection<Pair<RexNode, RexNode>> references = equalityTerms.get(ref1);
19911994
for (Pair<RexNode, RexNode> ref2 : references) {
1992-
final RexLiteral literal2 = equalityConstantTerms.get(ref2.left);
1993-
if (literal2 == null) {
1995+
final RexNode constant2 = equalityConstantTerms.get(ref2.left);
1996+
if (constant2 == null) {
19941997
continue;
19951998
}
1996-
if (literal1.getType().equals(literal2.getType()) && !literal1.equals(literal2)) {
1999+
if (constant1.getType().equals(constant2.getType())
2000+
&& !constantsEquivalent(constant1, constant2)) {
19972001
// If an expression is equal to two different constants,
19982002
// it is not satisfiable
19992003
return rexBuilder.makeLiteral(false);
@@ -3034,6 +3038,70 @@ private static class VariableCollector extends RexVisitorImpl<Void> {
30343038
}
30353039
}
30363040

3041+
private static final Set<SqlKind> CONSTANT_VALUE_CONSTRUCTOR_KINDS =
3042+
EnumSet.of(
3043+
SqlKind.ARRAY_VALUE_CONSTRUCTOR,
3044+
SqlKind.MULTISET_VALUE_CONSTRUCTOR);
3045+
3046+
private static @Nullable Pair<RexNode, RexNode> constantEquality(RexCall call) {
3047+
final RexNode o0 = call.getOperands().get(0);
3048+
final RexNode o1 = call.getOperands().get(1);
3049+
if (RexUtil.isReferenceOrAccess(o0, true) && isConstant(o1)) {
3050+
return Pair.of(o0, o1);
3051+
}
3052+
if (RexUtil.isReferenceOrAccess(o1, true) && isConstant(o0)) {
3053+
return Pair.of(o1, o0);
3054+
}
3055+
return null;
3056+
}
3057+
3058+
private static boolean constantsEquivalent(RexNode node1, RexNode node2) {
3059+
if (Objects.equals(node1, node2)) {
3060+
return true;
3061+
}
3062+
if (!(node1 instanceof RexCall) || !(node2 instanceof RexCall)) {
3063+
return false;
3064+
}
3065+
final RexCall call1 = (RexCall) node1;
3066+
final RexCall call2 = (RexCall) node2;
3067+
if (call1.getKind() != call2.getKind()) {
3068+
return false;
3069+
}
3070+
switch (call1.getKind()) {
3071+
case MULTISET_VALUE_CONSTRUCTOR:
3072+
return multisetLiteralEquals(call1, call2);
3073+
default:
3074+
return false;
3075+
}
3076+
}
3077+
3078+
private static boolean multisetLiteralEquals(RexCall left, RexCall right) {
3079+
return canonicalMultisetLiteral(left).equals(canonicalMultisetLiteral(right));
3080+
}
3081+
3082+
private static HashMultiset<Object> canonicalMultisetLiteral(RexCall call) {
3083+
final HashMultiset<Object> canonical = HashMultiset.create();
3084+
for (RexNode operand : call.getOperands()) {
3085+
canonical.add(canonicalMultisetOperand(operand));
3086+
}
3087+
return canonical;
3088+
}
3089+
3090+
private static Object canonicalMultisetOperand(RexNode operand) {
3091+
if (operand instanceof RexCall
3092+
&& operand.getKind() == SqlKind.MULTISET_VALUE_CONSTRUCTOR) {
3093+
return canonicalMultisetLiteral((RexCall) operand);
3094+
}
3095+
return operand;
3096+
}
3097+
3098+
private static boolean isConstant(RexNode node) {
3099+
return node instanceof RexLiteral
3100+
|| (node instanceof RexCall
3101+
&& CONSTANT_VALUE_CONSTRUCTOR_KINDS.contains(node.getKind())
3102+
&& RexUtil.isConstant(node));
3103+
}
3104+
30373105
/** Represents a simple Comparison.
30383106
*
30393107
* <p>Left hand side is a {@link RexNode}, right hand side is a literal.

core/src/test/java/org/apache/calcite/rex/RexProgramTest.java

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,6 +1876,159 @@ private void checkExponentialCnf(int n) {
18761876
checkSimplifyUnchanged(rexBuilder.makeCall(SqlStdOperatorTable.SOME_GT, operand1, operand2));
18771877
}
18781878

1879+
/** Unit test for
1880+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5733">[CALCITE-5733]
1881+
* Simplify 'a = ARRAY[1,2] AND a = ARRAY[2,3]' to 'false'</a>. */
1882+
@Test void testSimplifyArrayEquality() {
1883+
final RelDataType arrayType = tArray(tInt());
1884+
final RexNode aRef = input(arrayType, 0);
1885+
final RexNode array12 =
1886+
rexBuilder.makeCall(arrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1887+
ImmutableList.of(literal(1), literal(2)));
1888+
final RexNode array21 =
1889+
rexBuilder.makeCall(arrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1890+
ImmutableList.of(literal(2), literal(1)));
1891+
final RexNode array23 =
1892+
rexBuilder.makeCall(arrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1893+
ImmutableList.of(literal(2), literal(3)));
1894+
final RexNode array2Null =
1895+
rexBuilder.makeCall(arrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1896+
ImmutableList.of(literal(2), nullInt));
1897+
final RexNode arrayDoubleNull =
1898+
rexBuilder.makeCall(arrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1899+
ImmutableList.of(nullInt, nullInt));
1900+
1901+
// a = ARRAY[1,2] AND a = ARRAY[2,3]
1902+
final RexNode condition = and(eq(aRef, array12), eq(aRef, array23));
1903+
checkSimplifyFilter(condition, "false");
1904+
1905+
// a = ARRAY[1,2] AND a = ARRAY[2,null]
1906+
final RexNode condition2 = and(eq(aRef, array12), eq(aRef, array2Null));
1907+
checkSimplifyFilter(condition2, "false");
1908+
1909+
// a = ARRAY[1,2] AND a = ARRAY[2,null]
1910+
final RexNode condition3 = and(eq(aRef, array12), eq(aRef, arrayDoubleNull));
1911+
checkSimplifyFilter(condition3, "false");
1912+
1913+
// a = ARRAY[2,null] AND a = ARRAY[2,null]
1914+
final RexNode condition4 = and(eq(aRef, arrayDoubleNull), eq(aRef, arrayDoubleNull));
1915+
checkSimplifyFilter(condition4, "=($0, ARRAY(null:INTEGER, null:INTEGER))");
1916+
1917+
// a = ARRAY[1,2] AND a = ARRAY[2,1]
1918+
final RexNode condition5 = and(eq(aRef, array12), eq(aRef, array21));
1919+
checkSimplifyFilter(condition5, "false");
1920+
1921+
// Nested type for Array
1922+
final RelDataType nestedArrayType = tArray(arrayType);
1923+
final RexNode nestedRef = input(nestedArrayType, 1);
1924+
1925+
final RexNode nestedArray1212 =
1926+
rexBuilder.makeCall(nestedArrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1927+
ImmutableList.of(array12, array12));
1928+
final RexNode nestedArray1221 =
1929+
rexBuilder.makeCall(nestedArrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1930+
ImmutableList.of(array12, array21));
1931+
final RexNode nestedArray232Null =
1932+
rexBuilder.makeCall(nestedArrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1933+
ImmutableList.of(array23, array2Null));
1934+
final RexNode nestedArrayNulls =
1935+
rexBuilder.makeCall(nestedArrayType, SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR,
1936+
ImmutableList.of(arrayDoubleNull, arrayDoubleNull));
1937+
1938+
// a = ARRAY[ARRAY[1,2], ARRAY[1,2]] and a = ARRAY[ARRAY[1,2], ARRAY[2,1]]
1939+
final RexNode nestedCondition =
1940+
and(eq(nestedRef, nestedArray1212), eq(nestedRef, nestedArray1221));
1941+
checkSimplifyFilter(nestedCondition, "false");
1942+
1943+
// a = ARRAY[ARRAY[1,2], ARRAY[1,2]] and a = ARRAY[ARRAY[2,3], ARRAY[2,null]]
1944+
final RexNode nestedCondition2 =
1945+
and(eq(nestedRef, nestedArray1212), eq(nestedRef, nestedArray232Null));
1946+
checkSimplifyFilter(nestedCondition2, "false");
1947+
1948+
// a = ARRAY[ARRAY[1,2], ARRAY[1,2]] and a = ARRAY[ARRAY[null,null], ARRAY[null,null]]
1949+
final RexNode nestedCondition3 =
1950+
and(eq(nestedRef, nestedArray1212), eq(nestedRef, nestedArrayNulls));
1951+
checkSimplifyFilter(nestedCondition3, "false");
1952+
}
1953+
1954+
/** Unit test for
1955+
* <a href="https://issues.apache.org/jira/browse/CALCITE-5733">[CALCITE-5733]
1956+
* Simplify 'a = ARRAY[1,2] AND a = ARRAY[2,3]' to 'false'</a>. */
1957+
@Test void testSimplifyMultisetEquality() {
1958+
final RelDataType multisetType = typeFactory.createMultisetType(tInt(), -1);
1959+
final RexNode aRef = input(multisetType, 0);
1960+
final RexNode multiset12 =
1961+
rexBuilder.makeCall(multisetType, SqlStdOperatorTable.MULTISET_VALUE,
1962+
ImmutableList.of(literal(1), literal(2)));
1963+
final RexNode multiset21 =
1964+
rexBuilder.makeCall(multisetType, SqlStdOperatorTable.MULTISET_VALUE,
1965+
ImmutableList.of(literal(2), literal(1)));
1966+
final RexNode multiset23 =
1967+
rexBuilder.makeCall(multisetType, SqlStdOperatorTable.MULTISET_VALUE,
1968+
ImmutableList.of(literal(2), literal(3)));
1969+
final RexNode multiset2Null =
1970+
rexBuilder.makeCall(multisetType, SqlStdOperatorTable.MULTISET_VALUE,
1971+
ImmutableList.of(literal(2), nullInt));
1972+
final RexNode multisetDoubleNull =
1973+
rexBuilder.makeCall(multisetType, SqlStdOperatorTable.MULTISET_VALUE,
1974+
ImmutableList.of(nullInt, nullInt));
1975+
1976+
// a = MULTISET[1,2] AND a = MULTISET[2,3]
1977+
final RexNode condition = and(eq(aRef, multiset12), eq(aRef, multiset23));
1978+
checkSimplifyFilter(condition, "false");
1979+
1980+
// a = MULTISET[1,2] AND a = MULTISET[2,null]
1981+
final RexNode condition2 = and(eq(aRef, multiset12), eq(aRef, multiset2Null));
1982+
checkSimplifyFilter(condition2, "false");
1983+
1984+
// a = MULTISET[1,2] AND a = MULTISET[2,null]
1985+
final RexNode condition3 = and(eq(aRef, multiset12), eq(aRef, multisetDoubleNull));
1986+
checkSimplifyFilter(condition3, "false");
1987+
1988+
// a = MULTISET[2,null] AND a = MULTISET[2,null]
1989+
final RexNode condition4 = and(eq(aRef, multisetDoubleNull), eq(aRef, multisetDoubleNull));
1990+
checkSimplifyFilter(condition4, "=($0, MULTISET(null:INTEGER, null:INTEGER))");
1991+
1992+
// a = MULTISET[1,2] AND a = MULTISET[2,1]
1993+
final RexNode condition5 = and(eq(aRef, multiset12), eq(aRef, multiset21));
1994+
checkSimplifyFilter(condition5, "AND(=($0, MULTISET(1, 2)), =($0, MULTISET(2, 1)))");
1995+
1996+
// Nested type for Multiset
1997+
final RelDataType nestedMultisetType = typeFactory.createMultisetType(multisetType, -1);
1998+
final RexNode nestedRef = input(nestedMultisetType, 1);
1999+
2000+
final RexNode nestedMultiset1212 =
2001+
rexBuilder.makeCall(nestedMultisetType, SqlStdOperatorTable.MULTISET_VALUE,
2002+
ImmutableList.of(multiset12, multiset12));
2003+
final RexNode nestedMultiset1221 =
2004+
rexBuilder.makeCall(nestedMultisetType, SqlStdOperatorTable.MULTISET_VALUE,
2005+
ImmutableList.of(multiset12, multiset21));
2006+
final RexNode nestedMultiset232Null =
2007+
rexBuilder.makeCall(nestedMultisetType, SqlStdOperatorTable.MULTISET_VALUE,
2008+
ImmutableList.of(multiset23, multiset2Null));
2009+
final RexNode nestedMultisetNulls =
2010+
rexBuilder.makeCall(nestedMultisetType, SqlStdOperatorTable.MULTISET_VALUE,
2011+
ImmutableList.of(multisetDoubleNull, multisetDoubleNull));
2012+
2013+
// a = MULTISET[MULTISET[1,2], MULTISET[1,2]] and a = MULTISET[MULTISET[1,2], MULTISET[2,1]]
2014+
final RexNode nestedCondition =
2015+
and(eq(nestedRef, nestedMultiset1212), eq(nestedRef, nestedMultiset1221));
2016+
checkSimplifyFilter(nestedCondition,
2017+
"AND(=($1, MULTISET(MULTISET(1, 2), MULTISET(1, 2))),"
2018+
+ " =($1, MULTISET(MULTISET(1, 2), MULTISET(2, 1))))");
2019+
2020+
// a = MULTISET[MULTISET[1,2], MULTISET[1,2]] and a = MULTISET[MULTISET[2,3], MULTISET[2,null]]
2021+
final RexNode nestedCondition2 =
2022+
and(eq(nestedRef, nestedMultiset1212), eq(nestedRef, nestedMultiset232Null));
2023+
checkSimplifyFilter(nestedCondition2, "false");
2024+
2025+
// a = MULTISET[MULTISET[1,2], MULTISET[1,2]]
2026+
// and a = MULTISET[MULTISET[null,null], MULTISET[null,null]]
2027+
final RexNode nestedCondition3 =
2028+
and(eq(nestedRef, nestedMultiset1212), eq(nestedRef, nestedMultisetNulls));
2029+
checkSimplifyFilter(nestedCondition3, "false");
2030+
}
2031+
18792032
@Test void testSimplifyRange() {
18802033
final RexNode aRef = input(tInt(), 0);
18812034
// ((0 < a and a <= 10) or a >= 15) and a <> 6 and a <> 12

0 commit comments

Comments
 (0)