diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java index 4d7702295bf..4477c24a520 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java @@ -408,6 +408,22 @@ RexNode isFalse(RexNode e) { private RexNode simplifyGenericNode(RexCall e) { final List operands = new ArrayList<>(e.operands); simplifyList(operands, UNKNOWN); + + // Simplify CARDINALITY(MAP_KEYS(m)) -> CARDINALITY(m) + // and CARDINALITY(MAP_VALUES(m)) -> CARDINALITY(m) + if (e.getOperator().getName().equals("CARDINALITY") + && operands.size() == 1 + && operands.get(0) instanceof RexCall) { + final RexCall innerCall = (RexCall) operands.get(0); + final SqlKind innerKind = innerCall.getKind(); + if (innerKind == SqlKind.MAP_KEYS || innerKind == SqlKind.MAP_VALUES) { + // CARDINALITY(MAP_KEYS(m)) -> CARDINALITY(m) + // CARDINALITY(MAP_VALUES(m)) -> CARDINALITY(m) + final RexNode map = innerCall.getOperands().get(0); + return rexBuilder.makeCall(e.getParserPosition(), e.getOperator(), map); + } + } + if (e.operands.equals(operands)) { return e; } diff --git a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java index e23679eaf5f..a59b8949176 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java @@ -1524,4 +1524,64 @@ private static class UDT extends RelDataTypeImpl { literal, literal, flag), notNullValue()); } + + @Test void testCardinalityMapKeysSimplification() { + final RexImplicationCheckerFixtures.Fixture f = + new RexImplicationCheckerFixtures.Fixture(); + final RexBuilder rexBuilder = f.rexBuilder; + + // Create map literal: map['foo', 1, 'bar', 2] + final RexLiteral fooKey = rexBuilder.makeLiteral("foo"); + final RexLiteral oneVal = rexBuilder.makeExactLiteral(new java.math.BigDecimal(1)); + final RexLiteral barKey = rexBuilder.makeLiteral("bar"); + final RexLiteral twoVal = rexBuilder.makeExactLiteral(new java.math.BigDecimal(2)); + final RexNode mapLiteral = + rexBuilder.makeCall(SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR, + fooKey, oneVal, barKey, twoVal); + + // Create CARDINALITY(MAP_KEYS(map)) + final RexNode mapKeys = + rexBuilder.makeCall(SqlLibraryOperators.MAP_KEYS, mapLiteral); + final RexNode cardinalityMapKeys = + rexBuilder.makeCall(SqlStdOperatorTable.CARDINALITY, mapKeys); + + // Simplify + final RexNode simplified = f.simplify.simplify(cardinalityMapKeys); + + // Expected: CARDINALITY(map) + final RexNode expectedCardinalityMap = + rexBuilder.makeCall(SqlStdOperatorTable.CARDINALITY, mapLiteral); + + assertThat(simplified, hasToString(expectedCardinalityMap.toString())); + } + + @Test void testCardinalityMapValuesSimplification() { + final RexImplicationCheckerFixtures.Fixture f = + new RexImplicationCheckerFixtures.Fixture(); + final RexBuilder rexBuilder = f.rexBuilder; + + // Create map literal: map['foo', 1, 'bar', 2] + final RexLiteral fooKey = rexBuilder.makeLiteral("foo"); + final RexLiteral oneVal = rexBuilder.makeExactLiteral(new java.math.BigDecimal(1)); + final RexLiteral barKey = rexBuilder.makeLiteral("bar"); + final RexLiteral twoVal = rexBuilder.makeExactLiteral(new java.math.BigDecimal(2)); + final RexNode mapLiteral = + rexBuilder.makeCall(SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR, + fooKey, oneVal, barKey, twoVal); + + // Create CARDINALITY(MAP_VALUES(map)) + final RexNode mapValues = + rexBuilder.makeCall(SqlLibraryOperators.MAP_VALUES, mapLiteral); + final RexNode cardinalityMapValues = + rexBuilder.makeCall(SqlStdOperatorTable.CARDINALITY, mapValues); + + // Simplify + final RexNode simplified = f.simplify.simplify(cardinalityMapValues); + + // Expected: CARDINALITY(map) + final RexNode expectedCardinalityMap = + rexBuilder.makeCall(SqlStdOperatorTable.CARDINALITY, mapLiteral); + + assertThat(simplified, hasToString(expectedCardinalityMap.toString())); + } }