Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/src/main/java/org/apache/calcite/rex/RexInterpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,14 @@ private static Comparable ceil(RexCall call, List<Comparable> values) {
if (values.get(0) == N) {
return N;
}
if (values.size() == 1) {
if (values.get(0) instanceof BigDecimal) {
BigDecimal bigDecimal = (BigDecimal) values.get(0);
return bigDecimal.longValue();
} else {
return values.get(0);
}
}
final Long v = (Long) values.get(0);
final TimeUnitRange unit = (TimeUnitRange) values.get(1);
switch (unit) {
Expand Down
43 changes: 38 additions & 5 deletions core/src/main/java/org/apache/calcite/rex/RexSimplify.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ public class RexSimplify {

private static final Strong STRONG = new Strong();

private static final ImmutableList<SqlOperator> IDEMOTENT_UNARY_FUNCTIONS =
ImmutableList.of(SqlStdOperatorTable.UPPER,
SqlStdOperatorTable.LOWER,
SqlStdOperatorTable.INITCAP,
SqlStdOperatorTable.ABS,
SqlStdOperatorTable.FLOOR,
SqlStdOperatorTable.CEIL);

/**
* Creates a RexSimplify.
*
Expand Down Expand Up @@ -333,7 +341,8 @@ RexNode simplify(RexNode e, RexUnknownAs unknownAs) {
return simplifyM2v((RexCall) e);
default:
if (e.getClass() == RexCall.class) {
return simplifyGenericNode((RexCall) e);
RexCall rexCall = (RexCall) e;
return simplifyIdempotentUnaryFunction(rexCall);
} else {
return e;
}
Expand Down Expand Up @@ -397,6 +406,30 @@ RexNode isFalse(RexNode e) {
: rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, e);
}

/**
* Runs simplification unary function by eliminating idempotent.
*
* <p>Examples:
* <ul>
*
* <li>{@code abs(abs(abs(n))} returns {@code abs(n)}
*
* </ul>
*/
private RexNode simplifyIdempotentUnaryFunction(RexCall rexCall) {
while (IDEMOTENT_UNARY_FUNCTIONS.contains(rexCall.getOperator())
&& rexCall.getOperands().get(0) instanceof RexCall) {
RexCall subRexCall = (RexCall) rexCall.getOperands().get(0);
if (rexCall.getOperator() == subRexCall.getOperator()
&& rexCall.operands.size() == subRexCall.operands.size()) {
rexCall = subRexCall;
} else {
break;
}
}
return simplifyGenericNode(rexCall);
}

/**
* Runs simplification inside a non-specialized node.
*/
Expand Down Expand Up @@ -2373,7 +2406,7 @@ && sameTypeOrNarrowsNullability(e.getType(), intExpr.getType())) {
private RexNode simplifyCeilFloor(RexCall e) {
if (e.getOperands().size() != 2) {
// Bail out since we only simplify floor <date>
return e;
return simplifyIdempotentUnaryFunction(e);
}
final RexNode operand = simplify(e.getOperands().get(0), UNKNOWN);
if (e.getKind() == operand.getKind()) {
Expand All @@ -2382,7 +2415,7 @@ private RexNode simplifyCeilFloor(RexCall e) {
final RexCall child = (RexCall) operand;
if (child.getOperands().size() != 2) {
// Bail out since we only simplify ceil/floor <date>
return e;
return simplifyIdempotentUnaryFunction(e);
}
final RexLiteral parentFlag = (RexLiteral) e.operands.get(1);
final TimeUnitRange parentFlagValue = (TimeUnitRange) parentFlag.getValue();
Expand All @@ -2391,12 +2424,12 @@ private RexNode simplifyCeilFloor(RexCall e) {
if (parentFlagValue != null && childFlagValue != null) {
if (canRollUp(parentFlagValue.startUnit, childFlagValue.startUnit)) {
return e.clone(e.getType(),
ImmutableList.of(child.getOperands().get(0), parentFlag));
ImmutableList.of(simplify(child.getOperands().get(0)), parentFlag));
}
}
}
return e.clone(e.getType(),
ImmutableList.of(operand, e.getOperands().get(1)));
ImmutableList.of(simplify(operand), e.getOperands().get(1)));
}

/** Simplify TRIM function by eliminating nested duplication.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.calcite.rex.RexSimplify;
import org.apache.calcite.rex.RexUnknownAs;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.DateString;
Expand All @@ -35,9 +36,13 @@

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

import static org.apache.calcite.test.RexImplicationCheckerFixtures.Fixture;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasToString;

Expand Down Expand Up @@ -361,6 +366,89 @@ public class RexImplicationCheckerTest {
hasToString("2014"));
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-7122">[CALCITE-7122]
* Eliminate nested calls for idempotent unary functions UPPER/LOWER/ABS/INITCAP/CEIL/FLOOR</a>.
* */
@Test void testSimplifyIdempotentUnaryFunctions() {
// Test that:
// upper(upper(x)) is simplied to upper(x)
// lower(lower(x)) is simplied to lower(x)
// initcap(initcap(x)) is simplied to initcap(x)
final Fixture f = new Fixture();
List<SqlOperator> testOeratorList =
Arrays.asList(SqlStdOperatorTable.UPPER,
SqlStdOperatorTable.LOWER,
SqlStdOperatorTable.INITCAP);
for (SqlOperator operator : testOeratorList) {
RexCall innerCall =
(RexCall) f.rexBuilder
.makeCall(operator, f.rexBuilder.makeLiteral("Calcite Test"));
RexCall outerCall =
(RexCall) f.rexBuilder.makeCall(operator, innerCall);
RexCall simplifiedInnerCall =
(RexCall) f.simplify.simplifyPreservingType(outerCall,
RexUnknownAs.UNKNOWN, true);

assertThat(((RexLiteral) simplifiedInnerCall.getOperands().get(0))
.getValue(),
is(((RexLiteral) innerCall.getOperands().get(0)).getValue()));
}

// Test that:
// floor(floor(x)) is simplied to floor(x)
// ceil(ceil(x)) is simplied to ceil(x)
// abs(abs(x)) is simplied to abs(x)
testOeratorList =
Arrays.asList(SqlStdOperatorTable.ABS,
SqlStdOperatorTable.FLOOR,
SqlStdOperatorTable.CEIL);
for (SqlOperator operator : testOeratorList) {
RelDataType intType = f.rexBuilder.getTypeFactory().createSqlType(SqlTypeName.DECIMAL);
RexCall innerCall =
(RexCall) f.rexBuilder
.makeCall(operator, f.rexBuilder.makeLiteral(12, intType));
RexCall outerCall =
(RexCall) f.rexBuilder
.makeCall(operator, innerCall);
RexCall simplifiedInnerCall =
(RexCall) f.simplify.simplifyPreservingType(outerCall,
RexUnknownAs.UNKNOWN, true);
assertThat(((RexLiteral) simplifiedInnerCall.getOperands().get(0)).getValue(),
is(((RexLiteral) innerCall.getOperands().get(0)).getValue()));
}

// Test that max(abs(abs(x))) is simplied to max(abs(x))
RelDataType intType = f.rexBuilder.getTypeFactory().createSqlType(SqlTypeName.INTEGER);
RexCall innerCall =
(RexCall) f.rexBuilder
.makeCall(SqlStdOperatorTable.ABS, f.rexBuilder.makeLiteral(12, intType));
RexCall outerCall =
(RexCall) f.rexBuilder
.makeCall(SqlStdOperatorTable.ABS, innerCall);
RexCall maxCall =
(RexCall) f.rexBuilder
.makeCall(SqlStdOperatorTable.MAX, outerCall);
RexCall simplifiedInnerCall =
(RexCall) f.simplify.simplifyPreservingType(maxCall,
RexUnknownAs.UNKNOWN, true);
assertThat(simplifiedInnerCall.getOperator(), is(SqlStdOperatorTable.MAX));
assertThat(simplifiedInnerCall.getOperands().get(0), is(innerCall));
assertThat(simplifiedInnerCall.getOperands().get(0), not(outerCall));

// negative test
innerCall = (RexCall) f.rexBuilder
.makeCall(SqlStdOperatorTable.UPPER, f.rexBuilder.makeLiteral("Calcite Test"));
outerCall = (RexCall) f.rexBuilder.makeCall(SqlStdOperatorTable.LOWER, innerCall);
simplifiedInnerCall = (RexCall) f.simplify
.simplifyPreservingType(outerCall, RexUnknownAs.UNKNOWN, true);

assertThat(simplifiedInnerCall.getOperands().get(0),
not(((RexLiteral) innerCall.getOperands().get(0)).getValue()));
assertThat(((RexCall) simplifiedInnerCall.getOperands().get(0)).getOperands().get(0),
is(innerCall.getOperands().get(0)));
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-7042">[CALCITE-7042]
* Eliminate nested TRIM calls, exploiting the fact that TRIM is idempotent</a>. */
Expand Down
Loading