Skip to content

Commit

Permalink
Supports negative scale
Browse files Browse the repository at this point in the history
  • Loading branch information
NobiGo committed Sep 8, 2024
1 parent 35db766 commit 43a433b
Show file tree
Hide file tree
Showing 11 changed files with 569 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ protected DelegatingTypeSystem(RelDataTypeSystem typeSystem) {
}

@Override public boolean supportsNegativeScale() {
boolean isSupportNegativeScale = typeSystem.supportsNegativeScale();
if (isSupportNegativeScale) {
throw new AssertionError("For now, Calcite doesn't support negative scale");
}
return false;
return typeSystem.supportsNegativeScale();
}

@Override public RoundingMode roundingMode() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,18 @@ public interface RelDataTypeSystem {
int getMaxScale(SqlTypeName typeName);

/**
* Returns default precision for this type if supported, otherwise -1 if
* precision is either unsupported or must be specified explicitly.
* Returns default precision for this type if supported, otherwise
* {@link RelDataType#PRECISION_NOT_SPECIFIED}
* if precision is either unsupported or must be specified explicitly.
*
* @return Default precision
*/
int getDefaultPrecision(SqlTypeName typeName);

/**
* Returns the maximum precision (or length) allowed for this type, or -1 if
* precision/length are not applicable for this type.
* Returns the maximum precision (or length) allowed for this type, or
* {@link RelDataType#PRECISION_NOT_SPECIFIED}
* if precision/length are not applicable for this type.
*
* @return Maximum allowed precision
*/
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/java/org/apache/calcite/rex/RexBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,10 @@ protected RexLiteral makeLiteral(
case DECIMAL:
if (o != null && type.getScale() != RelDataType.SCALE_NOT_SPECIFIED) {
assert o instanceof BigDecimal;
o = ((BigDecimal) o).setScale(type.getScale(), RoundingMode.DOWN);
o = ((BigDecimal) o).setScale(type.getScale(), typeFactory.getTypeSystem().roundingMode());
if (type.getScale() < 0) {
o = new BigDecimal(((BigDecimal) o).toPlainString());
}
}
break;
default:
Expand Down
69 changes: 69 additions & 0 deletions core/src/test/java/org/apache/calcite/rex/RexBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.calcite.rel.type.RelDataTypeImpl;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.fun.SqlLibraryOperators;
Expand All @@ -53,6 +54,7 @@
import org.junit.jupiter.params.provider.MethodSource;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Calendar;
Expand Down Expand Up @@ -703,6 +705,73 @@ private void checkDate(RexLiteral literal) {
}
}

/** Tests {@link RexBuilder#makeExactLiteral(BigDecimal, RelDataType)}. */
@Test void testDecimalWithRoundingMode() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL, 4, 2);
final RexBuilder builder = new RexBuilder(typeFactory);
RexLiteral rexLiteral = builder.makeExactLiteral(new BigDecimal("13.556"), type);
assertThat(rexLiteral.getValue() instanceof BigDecimal, is(true));
assertThat(rexLiteral.getValue(), hasToString("13.55"));
final RelDataTypeFactory typeFactoryHalfUp =
new SqlTypeFactoryImpl(new RelDataTypeSystemImpl() {
@Override public RoundingMode roundingMode() {
return RoundingMode.HALF_UP;
}
});
final RelDataType typeHalfUp =
typeFactoryHalfUp.createSqlType(SqlTypeName.DECIMAL, 4, 2);
final RexBuilder builderHalfUp = new RexBuilder(typeFactoryHalfUp);
RexLiteral rexLiteralHalfUp =
builderHalfUp.makeExactLiteral(new BigDecimal("13.556"), typeHalfUp);
assertThat(rexLiteralHalfUp.getValue() instanceof BigDecimal, is(true));
assertThat(rexLiteralHalfUp.getValue(), hasToString("13.56"));
}

@Test void testDecimalWithNegativeScale() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
try {
final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL, 3, -2);
fail("expected exception, got " + type);
} catch (CalciteException e) {
assertThat(e.getMessage(),
containsString("DECIMAL scale -2 must be between greater than or equal to 0"));
}
}

@Test void testDecimalWithNegativeScaleRoundingHalfUp() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(new RelDataTypeSystemImpl() {
@Override public boolean supportsNegativeScale() {
return true;
}
@Override public RoundingMode roundingMode() {
return RoundingMode.HALF_UP;
}
});
final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL, 3, -2);
final RexBuilder builder = new RexBuilder(typeFactory);
RexLiteral rexLiteral = builder.makeLiteral(new BigDecimal("12355"), type);
assertThat(rexLiteral.getValue() instanceof BigDecimal, is(true));
assertThat(rexLiteral.getValue(), hasToString("12400"));
}

@Test void testDecimalWithNegativeScaleRoundingDown() {
final RelDataTypeFactory typeFactory =
new SqlTypeFactoryImpl(new RelDataTypeSystemImpl() {
@Override public boolean supportsNegativeScale() {
return true;
}
});
final RelDataType type = typeFactory.createSqlType(SqlTypeName.DECIMAL, 3, -2);
final RexBuilder builder = new RexBuilder(typeFactory);
RexLiteral rexLiteralHalfUp = builder.makeLiteral(new BigDecimal("12355"), type);
assertThat(rexLiteralHalfUp.getValue() instanceof BigDecimal, is(true));
assertThat(rexLiteralHalfUp.getValue(), hasToString("12300"));
}

/** Tests {@link DateString} year range. */
@Test void testDateStringYearError() {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ static class Fixture extends SqlTypeFixture {
});
assertThrows(CalciteException.class, () ->
customTypeFactory.createSqlType(SqlTypeName.DECIMAL, 10, -5),
"For now, Calcite doesn't support negative scale");
"DECIMAL scale -5 must be between greater than or equal to 0");
}

@Test void testDecimalAdditionReturnTypeInference() {
Expand Down
18 changes: 18 additions & 0 deletions core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ public static void main(String[] args) throws Exception {
CustomRelDataTypeSystem.class.getName() + "#ROUNDING_MODE_HALF_UP")
.with(CalciteAssert.Config.SCOTT)
.connect();
case "scott-negative-scale":
return CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteConnectionProperty.TYPE_SYSTEM,
CustomRelDataTypeSystem.class.getName() + "#NEGATIVE_SCALE")
.with(CalciteAssert.Config.SCOTT)
.connect();
case "scott-negative-scale-rounding-half-up":
return CalciteAssert.that()
.with(CalciteConnectionProperty.PARSER_FACTORY,
ExtensionDdlExecutor.class.getName() + "#PARSER_FACTORY")
.with(CalciteConnectionProperty.FUN, SqlLibrary.CALCITE.fun)
.with(CalciteConnectionProperty.TYPE_SYSTEM,
CustomRelDataTypeSystem.class.getName() + "#NEGATIVE_SCALE_ROUNDING_MODE_HALF_UP")
.with(CalciteAssert.Config.SCOTT)
.connect();
case "scott-lenient":
// Same as "scott", but uses LENIENT conformance.
// TODO: add a way to change conformance without defining a new
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,43 @@

/**
* Custom type system only for Quidem test.
*
* <p> Specify the rounding behaviour. In the default implementation,
* the rounding mode is {@link RoundingMode#DOWN}, but here is {@link RoundingMode#HALF_UP}
*
* <p>The default implementation is {@link #DEFAULT}.
*/

public class CustomRelDataTypeSystem extends RelDataTypeSystemImpl {
public final class CustomRelDataTypeSystem {

public static final RelDataTypeSystem ROUNDING_MODE_HALF_UP = new CustomRelDataTypeSystem();

@Override public RoundingMode roundingMode() {
return RoundingMode.HALF_UP;
private CustomRelDataTypeSystem() {
}

/**
* Specify the rounding behaviour. In the default implementation,
* the rounding mode is {@link RoundingMode#DOWN}, but here is {@link RoundingMode#HALF_UP}.
*/
public static final RelDataTypeSystem ROUNDING_MODE_HALF_UP = new RelDataTypeSystemImpl() {
@Override public RoundingMode roundingMode() {
return RoundingMode.HALF_UP;
}
};

/**
* Supports negative scale.
*/
public static final RelDataTypeSystem NEGATIVE_SCALE = new RelDataTypeSystemImpl() {
@Override public boolean supportsNegativeScale() {
return true;
}
};

/**
* Supports negative scale and rounding mode is {@link RoundingMode#HALF_UP}.
*/
public static final RelDataTypeSystem NEGATIVE_SCALE_ROUNDING_MODE_HALF_UP =
new RelDataTypeSystemImpl() {
@Override public boolean supportsNegativeScale() {
return true;
}
@Override public RoundingMode roundingMode() {
return RoundingMode.HALF_UP;
}
};

}
Loading

0 comments on commit 43a433b

Please sign in to comment.