Skip to content

Commit 974f5f1

Browse files
committed
[CALCITE-7122] Eliminate nested calls for idempotent unary functions UPPER/LOWER/ABS/INITCAP
1 parent 0071f17 commit 974f5f1

File tree

2 files changed

+97
-1
lines changed

2 files changed

+97
-1
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ public class RexSimplify {
8888

8989
private static final Strong STRONG = new Strong();
9090

91+
private static final ImmutableList<SqlOperator> IDEMOTENT_UNARY_FUNCTIONS =
92+
ImmutableList.of(SqlStdOperatorTable.UPPER,
93+
SqlStdOperatorTable.LOWER,
94+
SqlStdOperatorTable.INITCAP,
95+
SqlStdOperatorTable.ABS);
96+
9197
/**
9298
* Creates a RexSimplify.
9399
*
@@ -333,7 +339,9 @@ RexNode simplify(RexNode e, RexUnknownAs unknownAs) {
333339
return simplifyM2v((RexCall) e);
334340
default:
335341
if (e.getClass() == RexCall.class) {
336-
return simplifyGenericNode((RexCall) e);
342+
RexCall rexCall = (RexCall) e;
343+
rexCall = simplifyIdempotentUnaryFunction(rexCall);
344+
return simplifyGenericNode(rexCall);
337345
} else {
338346
return e;
339347
}
@@ -397,6 +405,22 @@ RexNode isFalse(RexNode e) {
397405
: rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, e);
398406
}
399407

408+
/**
409+
* Runs simplification unary function by eliminating idempotent.
410+
*/
411+
private RexCall simplifyIdempotentUnaryFunction(RexCall rexCall) {
412+
while (IDEMOTENT_UNARY_FUNCTIONS.contains(rexCall.getOperator())
413+
&& rexCall.getOperands().get(0) instanceof RexCall) {
414+
RexCall subRexCall = (RexCall) rexCall.getOperands().get(0);
415+
if (rexCall.getOperator() == subRexCall.getOperator()) {
416+
rexCall = subRexCall;
417+
} else {
418+
break;
419+
}
420+
}
421+
return rexCall;
422+
}
423+
400424
/**
401425
* Runs simplification inside a non-specialized node.
402426
*/

core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.calcite.rex.RexSimplify;
2525
import org.apache.calcite.rex.RexUnknownAs;
2626
import org.apache.calcite.sql.SqlKind;
27+
import org.apache.calcite.sql.SqlOperator;
2728
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
2829
import org.apache.calcite.sql.type.SqlTypeName;
2930
import org.apache.calcite.util.DateString;
@@ -35,9 +36,13 @@
3536

3637
import org.junit.jupiter.api.Test;
3738

39+
import java.util.Arrays;
40+
import java.util.List;
41+
3842
import static org.apache.calcite.test.RexImplicationCheckerFixtures.Fixture;
3943

4044
import static org.hamcrest.CoreMatchers.is;
45+
import static org.hamcrest.CoreMatchers.not;
4146
import static org.hamcrest.MatcherAssert.assertThat;
4247
import static org.hamcrest.Matchers.hasToString;
4348

@@ -361,6 +366,73 @@ public class RexImplicationCheckerTest {
361366
hasToString("2014"));
362367
}
363368

369+
/** Test case for
370+
* <a href="https://issues.apache.org/jira/browse/CALCITE-7122">[CALCITE-7122]
371+
* Eliminate nested calls for idempotent unary functions UPPER/LOWER/ABS/INITCAP</a>. */
372+
@Test void testSimplifyIdempotentUnaryFunctions() {
373+
// Test that:
374+
// upper(upper(x)) is simplied to upper(x)
375+
// lower(upper(x)) is simplied to lower(x)
376+
// initcap(upper(x)) is simplied to initcap(x)
377+
final Fixture f = new Fixture();
378+
List<SqlOperator> testOeratorList =
379+
Arrays.asList(SqlStdOperatorTable.UPPER,
380+
SqlStdOperatorTable.LOWER,
381+
SqlStdOperatorTable.INITCAP);
382+
for (SqlOperator operator : testOeratorList) {
383+
RexCall innerCall =
384+
(RexCall) f.rexBuilder
385+
.makeCall(operator, f.rexBuilder.makeLiteral("Calcite Test"));
386+
RexCall outerCall =
387+
(RexCall) f.rexBuilder.makeCall(operator, innerCall);
388+
RexCall simplifiedInnerCall =
389+
(RexCall) f.simplify.simplifyPreservingType(outerCall,
390+
RexUnknownAs.UNKNOWN, true);
391+
392+
assertThat(((RexLiteral) simplifiedInnerCall.getOperands().get(0))
393+
.getValue(),
394+
is(((RexLiteral) innerCall.getOperands().get(0)).getValue()));
395+
}
396+
397+
// Test that abs(abs(x)) is simplied to abs(x)
398+
RelDataType intType = f.rexBuilder.getTypeFactory().createSqlType(SqlTypeName.INTEGER);
399+
RexCall innerCall =
400+
(RexCall) f.rexBuilder
401+
.makeCall(SqlStdOperatorTable.ABS, f.rexBuilder.makeLiteral(12, intType));
402+
RexCall outerCall =
403+
(RexCall) f.rexBuilder
404+
.makeCall(SqlStdOperatorTable.ABS, innerCall);
405+
RexCall simplifiedInnerCall =
406+
(RexCall) f.simplify.simplifyPreservingType(outerCall,
407+
RexUnknownAs.UNKNOWN, true);
408+
409+
assertThat(((RexLiteral) simplifiedInnerCall.getOperands().get(0)).getValue(),
410+
is(((RexLiteral) innerCall.getOperands().get(0)).getValue()));
411+
412+
// Test that max(abs(abs(x))) is simplied to max(abs(x))
413+
RexCall maxCall =
414+
(RexCall) f.rexBuilder
415+
.makeCall(SqlStdOperatorTable.MAX, outerCall);
416+
simplifiedInnerCall =
417+
(RexCall) f.simplify.simplifyPreservingType(maxCall,
418+
RexUnknownAs.UNKNOWN, true);
419+
assertThat(simplifiedInnerCall.getOperator(), is(SqlStdOperatorTable.MAX));
420+
assertThat(simplifiedInnerCall.getOperands().get(0), is(innerCall));
421+
assertThat(simplifiedInnerCall.getOperands().get(0), not(outerCall));
422+
423+
// negative test
424+
innerCall = (RexCall) f.rexBuilder
425+
.makeCall(SqlStdOperatorTable.UPPER, f.rexBuilder.makeLiteral("Calcite Test"));
426+
outerCall = (RexCall) f.rexBuilder.makeCall(SqlStdOperatorTable.LOWER, innerCall);
427+
simplifiedInnerCall = (RexCall) f.simplify
428+
.simplifyPreservingType(outerCall, RexUnknownAs.UNKNOWN, true);
429+
430+
assertThat(simplifiedInnerCall.getOperands().get(0),
431+
not(((RexLiteral) innerCall.getOperands().get(0)).getValue()));
432+
assertThat(((RexCall) simplifiedInnerCall.getOperands().get(0)).getOperands().get(0),
433+
is(innerCall.getOperands().get(0)));
434+
}
435+
364436
/** Test case for
365437
* <a href="https://issues.apache.org/jira/browse/CALCITE-7042">[CALCITE-7042]
366438
* Eliminate nested TRIM calls, exploiting the fact that TRIM is idempotent</a>. */

0 commit comments

Comments
 (0)