blob: 75932b5bf228a49c4f2d9fe67fc63bdc8f1b64c6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.test;
import org.apache.calcite.avatica.util.TimeUnitRange;
import org.apache.calcite.plan.RexImplicationChecker;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexSimplify;
import org.apache.calcite.rex.RexUnknownAs;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.util.DateString;
import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import org.junit.jupiter.api.Test;
import static org.apache.calcite.test.RexImplicationCheckerFixtures.Fixture;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
/**
* Unit tests for {@link RexImplicationChecker}.
*/
public class RexImplicationCheckerTest {
//~ Instance fields --------------------------------------------------------
//~ Methods ----------------------------------------------------------------
// Simple Tests for Operators
@Test void testSimpleGreaterCond() {
final Fixture f = new Fixture();
final RexNode iGt10 = f.gt(f.i, f.literal(10));
final RexNode iGt30 = f.gt(f.i, f.literal(30));
final RexNode iGe30 = f.ge(f.i, f.literal(30));
final RexNode iGe10 = f.ge(f.i, f.literal(10));
final RexNode iEq30 = f.eq(f.i, f.literal(30));
final RexNode iNe10 = f.ne(f.i, f.literal(10));
f.checkImplies(iGt30, iGt10);
f.checkNotImplies(iGt10, iGt30);
f.checkNotImplies(iGt10, iGe30);
f.checkImplies(iGe30, iGt10);
f.checkImplies(iEq30, iGt10);
f.checkNotImplies(iGt10, iEq30);
f.checkNotImplies(iGt10, iNe10);
f.checkNotImplies(iGe10, iNe10);
// identity
f.checkImplies(iGt10, iGt10);
f.checkImplies(iGe30, iGe30);
}
@Test void testSimpleLesserCond() {
final Fixture f = new Fixture();
final RexNode iLt10 = f.lt(f.i, f.literal(10));
final RexNode iLt30 = f.lt(f.i, f.literal(30));
final RexNode iLe30 = f.le(f.i, f.literal(30));
final RexNode iLe10 = f.le(f.i, f.literal(10));
final RexNode iEq10 = f.eq(f.i, f.literal(10));
final RexNode iNe10 = f.ne(f.i, f.literal(10));
f.checkImplies(iLt10, iLt30);
f.checkNotImplies(iLt30, iLt10);
f.checkImplies(iLt10, iLe30);
f.checkNotImplies(iLe30, iLt10);
f.checkImplies(iEq10, iLt30);
f.checkNotImplies(iLt30, iEq10);
f.checkNotImplies(iLt10, iEq10);
f.checkNotImplies(iLt10, iNe10);
f.checkNotImplies(iLe10, iNe10);
// identity
f.checkImplies(iLt10, iLt10);
f.checkImplies(iLe30, iLe30);
}
@Test void testSimpleEq() {
final Fixture f = new Fixture();
final RexNode iEq30 = f.eq(f.i, f.literal(30));
final RexNode iNe10 = f.ne(f.i, f.literal(10));
final RexNode iNe30 = f.ne(f.i, f.literal(30));
f.checkImplies(iEq30, iEq30);
f.checkImplies(iNe10, iNe10);
f.checkImplies(iEq30, iNe10);
f.checkNotImplies(iNe10, iEq30);
f.checkNotImplies(iNe30, iEq30);
}
// Simple Tests for DataTypes
@Test void testSimpleDec() {
final Fixture f = new Fixture();
final RexNode node1 = f.lt(f.dec, f.floatLiteral(30.9));
final RexNode node2 = f.lt(f.dec, f.floatLiteral(40.33));
f.checkImplies(node1, node2);
f.checkNotImplies(node2, node1);
}
@Test void testSimpleBoolean() {
final Fixture f = new Fixture();
final RexNode bEqTrue = f.eq(f.bl, f.rexBuilder.makeLiteral(true));
final RexNode bEqFalse = f.eq(f.bl, f.rexBuilder.makeLiteral(false));
// TODO: Need to support false => true
//f.checkImplies(bEqFalse, bEqTrue);
f.checkNotImplies(bEqTrue, bEqFalse);
}
@Test void testSimpleLong() {
final Fixture f = new Fixture();
final RexNode xGeBig = f.ge(f.lg, f.longLiteral(324324L));
final RexNode xGtBigger = f.gt(f.lg, f.longLiteral(324325L));
final RexNode xGeBigger = f.ge(f.lg, f.longLiteral(324325L));
f.checkImplies(xGtBigger, xGeBig);
f.checkImplies(xGtBigger, xGeBigger);
f.checkImplies(xGeBigger, xGeBig);
f.checkNotImplies(xGeBig, xGtBigger);
}
@Test void testSimpleShort() {
final Fixture f = new Fixture();
final RexNode xGe10 = f.ge(f.sh, f.shortLiteral((short) 10));
final RexNode xGe11 = f.ge(f.sh, f.shortLiteral((short) 11));
f.checkImplies(xGe11, xGe10);
f.checkNotImplies(xGe10, xGe11);
}
@Test void testSimpleChar() {
final Fixture f = new Fixture();
final RexNode xGeB = f.ge(f.ch, f.charLiteral("b"));
final RexNode xGeA = f.ge(f.ch, f.charLiteral("a"));
f.checkImplies(xGeB, xGeA);
f.checkNotImplies(xGeA, xGeB);
}
@Test void testSimpleString() {
final Fixture f = new Fixture();
final RexNode node1 = f.eq(f.str, f.rexBuilder.makeLiteral("en"));
f.checkImplies(node1, node1);
}
@Test void testSimpleDate() {
final Fixture f = new Fixture();
final DateString d = DateString.fromCalendarFields(Util.calendar());
final RexNode node1 = f.ge(f.d, f.dateLiteral(d));
final RexNode node2 = f.eq(f.d, f.dateLiteral(d));
f.checkImplies(node2, node1);
f.checkNotImplies(node1, node2);
final DateString dBeforeEpoch1 = DateString.fromDaysSinceEpoch(-12345);
final DateString dBeforeEpoch2 = DateString.fromDaysSinceEpoch(-123);
final RexNode nodeBe1 = f.lt(f.d, f.dateLiteral(dBeforeEpoch1));
final RexNode nodeBe2 = f.lt(f.d, f.dateLiteral(dBeforeEpoch2));
f.checkImplies(nodeBe1, nodeBe2);
f.checkNotImplies(nodeBe2, nodeBe1);
}
@Test void testSimpleTimeStamp() {
final Fixture f = new Fixture();
final TimestampString ts =
TimestampString.fromCalendarFields(Util.calendar());
final RexNode node1 = f.lt(f.ts, f.timestampLiteral(ts));
final RexNode node2 = f.le(f.ts, f.timestampLiteral(ts));
f.checkImplies(node1, node2);
f.checkNotImplies(node2, node1);
final TimestampString tsBeforeEpoch1 =
TimestampString.fromMillisSinceEpoch(-1234567890L);
final TimestampString tsBeforeEpoch2 =
TimestampString.fromMillisSinceEpoch(-1234567L);
final RexNode nodeBe1 = f.lt(f.ts, f.timestampLiteral(tsBeforeEpoch1));
final RexNode nodeBe2 = f.lt(f.ts, f.timestampLiteral(tsBeforeEpoch2));
f.checkImplies(nodeBe1, nodeBe2);
f.checkNotImplies(nodeBe2, nodeBe1);
}
@Test void testSimpleTime() {
final Fixture f = new Fixture();
final TimeString t = TimeString.fromCalendarFields(Util.calendar());
final RexNode node1 = f.lt(f.t, f.timeLiteral(t));
final RexNode node2 = f.le(f.t, f.timeLiteral(t));
f.checkImplies(node1, node2);
f.checkNotImplies(node2, node1);
}
@Test void testSimpleBetween() {
final Fixture f = new Fixture();
final RexNode iGe30 = f.ge(f.i, f.literal(30));
final RexNode iLt70 = f.lt(f.i, f.literal(70));
final RexNode iGe30AndLt70 = f.and(iGe30, iLt70);
final RexNode iGe50 = f.ge(f.i, f.literal(50));
final RexNode iLt60 = f.lt(f.i, f.literal(60));
final RexNode iGe50AndLt60 = f.and(iGe50, iLt60);
f.checkNotImplies(iGe30AndLt70, iGe50);
f.checkNotImplies(iGe30AndLt70, iLt60);
f.checkNotImplies(iGe30AndLt70, iGe50AndLt60);
f.checkNotImplies(iGe30, iGe50AndLt60);
f.checkNotImplies(iLt70, iGe50AndLt60);
f.checkImplies(iGe50AndLt60, iGe30AndLt70);
f.checkImplies(iGe50AndLt60, iLt70);
f.checkImplies(iGe50AndLt60, iGe30);
}
@Test void testSimpleBetweenCornerCases() {
final Fixture f = new Fixture();
final RexNode node1 = f.gt(f.i, f.literal(30));
final RexNode node2 = f.gt(f.i, f.literal(50));
final RexNode node3 = f.lt(f.i, f.literal(60));
final RexNode node4 = f.lt(f.i, f.literal(80));
final RexNode node5 = f.lt(f.i, f.literal(90));
final RexNode node6 = f.lt(f.i, f.literal(100));
f.checkNotImplies(f.and(node1, node2), f.and(node3, node4));
f.checkNotImplies(f.and(node5, node6), f.and(node3, node4));
f.checkNotImplies(f.and(node1, node2), node6);
f.checkNotImplies(node6, f.and(node1, node2));
f.checkImplies(f.and(node3, node4), f.and(node5, node6));
}
/** Similar to {@link MaterializedViewSubstitutionVisitorTest#testAlias()}:
* {@code x > 1 OR (y > 2 AND z > 4)}
* implies
* {@code (y > 3 AND z > 5)}. */
@Test void testOr() {
final Fixture f = new Fixture();
final RexNode xGt1 = f.gt(f.i, f.literal(1));
final RexNode yGt2 = f.gt(f.dec, f.literal(2));
final RexNode yGt3 = f.gt(f.dec, f.literal(3));
final RexNode zGt4 = f.gt(f.lg, f.literal(4));
final RexNode zGt5 = f.gt(f.lg, f.literal(5));
final RexNode yGt2AndZGt4 = f.and(yGt2, zGt4);
final RexNode yGt3AndZGt5 = f.and(yGt3, zGt5);
final RexNode or = f.or(xGt1, yGt2AndZGt4);
//f.checkNotImplies(or, yGt3AndZGt5);
f.checkImplies(yGt3AndZGt5, or);
}
@Test void testNotNull() {
final Fixture f = new Fixture();
final RexNode node1 = f.eq(f.str, f.rexBuilder.makeLiteral("en"));
final RexNode node2 = f.notNull(f.str);
final RexNode node3 = f.gt(f.str, f.rexBuilder.makeLiteral("abc"));
f.checkImplies(node1, node2);
f.checkNotImplies(node2, node1);
f.checkImplies(node3, node2);
f.checkImplies(node2, node2);
}
@Test void testIsNull() {
final Fixture f = new Fixture();
final RexNode sEqEn = f.eq(f.str, f.charLiteral("en"));
final RexNode sIsNotNull = f.notNull(f.str);
final RexNode sIsNull = f.isNull(f.str);
final RexNode iEq5 = f.eq(f.i, f.literal(5));
final RexNode iIsNull = f.isNull(f.i);
final RexNode iIsNotNull = f.notNull(f.i);
f.checkNotImplies(sIsNotNull, sIsNull);
f.checkNotImplies(sIsNull, sIsNotNull);
f.checkNotImplies(sEqEn, sIsNull);
f.checkNotImplies(sIsNull, sEqEn);
f.checkImplies(sEqEn, sIsNotNull); // "s = literal" implies "s is not null"
f.checkImplies(sIsNotNull, sIsNotNull); // "s is not null" implies "s is not null"
f.checkImplies(sIsNull, sIsNull); // "s is null" implies "s is null"
// "s is not null and y = 5" implies "s is not null"
f.checkImplies(f.and(sIsNotNull, iEq5), sIsNotNull);
// "y = 5 and s is not null" implies "s is not null"
f.checkImplies(f.and(iEq5, sIsNotNull), sIsNotNull);
// "y is not null" does not imply "s is not null"
f.checkNotImplies(iIsNull, sIsNotNull);
// "s is not null or i = 5" does not imply "s is not null"
f.checkNotImplies(f.or(sIsNotNull, iEq5), sIsNotNull);
// "s is not null" implies "s is not null or i = 5"
f.checkImplies(sIsNotNull, f.or(sIsNotNull, iEq5));
// "s is not null" implies "i = 5 or s is not null"
f.checkImplies(sIsNotNull, f.or(iEq5, sIsNotNull));
// "i > 10" implies "x is not null"
f.checkImplies(f.gt(f.i, f.literal(10)), iIsNotNull);
// "-20 > i" implies "x is not null"
f.checkImplies(f.gt(f.literal(-20), f.i), iIsNotNull);
// "s is null and -20 > i" implies "x is not null"
f.checkImplies(f.and(sIsNull, f.gt(f.literal(-20), f.i)), iIsNotNull);
// "i > 10" does not imply "x is null"
f.checkNotImplies(f.gt(f.i, f.literal(10)), iIsNull);
}
/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-2041">[CALCITE-2041]
* When simplifying a nullable expression, allow the result to change type to
* NOT NULL</a> and match nullability.
*
* @see RexSimplify#simplifyPreservingType(RexNode, RexUnknownAs, boolean) */
@Test void testSimplifyCastMatchNullability() {
final Fixture f = new Fixture();
// The cast is nullable, while the literal is not nullable. When we simplify
// it, we end up with the literal. If defaultSimplifier is used, a CAST is
// introduced on top of the expression, as nullability of the new expression
// does not match the nullability of the original one. If
// nonMatchingNullabilitySimplifier is used, the CAST is not added and the
// simplified expression only consists of the literal.
final RexNode e = f.cast(f.intRelDataType, f.literal(2014));
assertThat(
f.simplify.simplifyPreservingType(e, RexUnknownAs.UNKNOWN, true)
.toString(),
is("CAST(2014):JavaType(class java.lang.Integer)"));
assertThat(
f.simplify.simplifyPreservingType(e, RexUnknownAs.UNKNOWN, false)
.toString(),
is("2014"));
// In this case, the cast is not nullable. Thus, in both cases, the
// simplified expression only consists of the literal.
RelDataType notNullIntRelDataType = f.typeFactory.createJavaType(int.class);
final RexNode e2 = f.cast(notNullIntRelDataType,
f.cast(notNullIntRelDataType, f.literal(2014)));
assertThat(
f.simplify.simplifyPreservingType(e2, RexUnknownAs.UNKNOWN, true)
.toString(),
is("2014"));
assertThat(
f.simplify.simplifyPreservingType(e2, RexUnknownAs.UNKNOWN, false)
.toString(),
is("2014"));
}
/** Test case for simplifier of ceil/floor. */
@Test void testSimplifyCeilFloor() {
// We can add more time units here once they are supported in
// RexInterpreter, e.g., TimeUnitRange.HOUR, TimeUnitRange.MINUTE,
// TimeUnitRange.SECOND.
final ImmutableList<TimeUnitRange> timeUnitRanges =
ImmutableList.of(TimeUnitRange.YEAR, TimeUnitRange.MONTH);
final Fixture f = new Fixture();
final RexNode literalTs =
f.timestampLiteral(new TimestampString("2010-10-10 00:00:00"));
for (int i = 0; i < timeUnitRanges.size(); i++) {
final RexNode innerFloorCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.FLOOR, literalTs,
f.rexBuilder.makeFlag(timeUnitRanges.get(i)));
final RexNode innerCeilCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.CEIL, literalTs,
f.rexBuilder.makeFlag(timeUnitRanges.get(i)));
for (int j = 0; j <= i; j++) {
final RexNode outerFloorCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.FLOOR, innerFloorCall,
f.rexBuilder.makeFlag(timeUnitRanges.get(j)));
final RexNode outerCeilCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.CEIL, innerCeilCall,
f.rexBuilder.makeFlag(timeUnitRanges.get(j)));
final RexCall floorSimplifiedExpr =
(RexCall) f.simplify.simplifyPreservingType(outerFloorCall,
RexUnknownAs.UNKNOWN, true);
assertThat(floorSimplifiedExpr.getKind(), is(SqlKind.FLOOR));
assertThat(((RexLiteral) floorSimplifiedExpr.getOperands().get(1))
.getValue().toString(),
is(timeUnitRanges.get(j).toString()));
assertThat(floorSimplifiedExpr.getOperands().get(0).toString(),
is(literalTs.toString()));
final RexCall ceilSimplifiedExpr =
(RexCall) f.simplify.simplifyPreservingType(outerCeilCall,
RexUnknownAs.UNKNOWN, true);
assertThat(ceilSimplifiedExpr.getKind(), is(SqlKind.CEIL));
assertThat(((RexLiteral) ceilSimplifiedExpr.getOperands().get(1)).getValue().toString(),
is(timeUnitRanges.get(j).toString()));
assertThat(ceilSimplifiedExpr.getOperands().get(0).toString(), is(literalTs.toString()));
}
}
// Negative test
for (int i = timeUnitRanges.size() - 1; i >= 0; i--) {
final RexNode innerFloorCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.FLOOR, literalTs,
f.rexBuilder.makeFlag(timeUnitRanges.get(i)));
final RexNode innerCeilCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.CEIL, literalTs,
f.rexBuilder.makeFlag(timeUnitRanges.get(i)));
for (int j = timeUnitRanges.size() - 1; j > i; j--) {
final RexNode outerFloorCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.FLOOR, innerFloorCall,
f.rexBuilder.makeFlag(timeUnitRanges.get(j)));
final RexNode outerCeilCall = f.rexBuilder.makeCall(
SqlStdOperatorTable.CEIL, innerCeilCall,
f.rexBuilder.makeFlag(timeUnitRanges.get(j)));
final RexCall floorSimplifiedExpr =
(RexCall) f.simplify.simplifyPreservingType(outerFloorCall,
RexUnknownAs.UNKNOWN, true);
assertThat(floorSimplifiedExpr.toString(), is(outerFloorCall.toString()));
final RexCall ceilSimplifiedExpr =
(RexCall) f.simplify.simplifyPreservingType(outerCeilCall,
RexUnknownAs.UNKNOWN, true);
assertThat(ceilSimplifiedExpr.toString(), is(outerCeilCall.toString()));
}
}
}
}