Skip to content

Commit

Permalink
[CALCITE-6238] Exception while evaluating ROUND/TRUNCATE functions
Browse files Browse the repository at this point in the history
Signed-off-by: Mihai Budiu <[email protected]>
  • Loading branch information
mihaibudiu committed Feb 8, 2024
1 parent f837ffa commit 2e384ed
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1774,11 +1774,11 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
OperandTypes.NUMERIC,
SqlFunctionCategory.NUMERIC);

/** The {@code ROUND(numeric [, numeric])} function. */
/** The {@code ROUND(numeric [, integer])} function. */
public static final SqlFunction ROUND =
SqlBasicFunction.create("ROUND",
ReturnTypes.ARG0_NULLABLE,
OperandTypes.NUMERIC_OPTIONAL_INTEGER,
OperandTypes.NUMERIC.or(OperandTypes.NUMERIC_INT32),
SqlFunctionCategory.NUMERIC);

/** The {@code SIGN(numeric)} function. */
Expand All @@ -1802,11 +1802,11 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
OperandTypes.NUMERIC,
SqlFunctionCategory.NUMERIC);

/** The {@code TRUNCATE(numeric [, numeric])} function. */
/** The {@code TRUNCATE(numeric [, integer])} function. */
public static final SqlBasicFunction TRUNCATE =
SqlBasicFunction.create("TRUNCATE",
ReturnTypes.ARG0_NULLABLE,
OperandTypes.NUMERIC_OPTIONAL_INTEGER,
OperandTypes.NUMERIC.or(OperandTypes.NUMERIC_INT32),
SqlFunctionCategory.NUMERIC);

/** The {@code PI} function. */
Expand Down
19 changes: 19 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,18 @@ public static SqlOperandTypeChecker sequence(String allowedSignatures,
ImmutableList.copyOf(rules), allowedSignatures, null, null);
}

/**
* Creates an operand checker from a sequence of single-operand checkers,
* generating the signature from the components.
*/
public static SqlOperandTypeChecker sequence(
BiFunction<SqlOperator, String, String> signatureGenerator,
SqlSingleOperandTypeChecker... rules) {
return new CompositeOperandTypeChecker(
CompositeOperandTypeChecker.Composition.SEQUENCE,
ImmutableList.copyOf(rules), null, signatureGenerator, null);
}

/**
* Creates a checker that passes if all of the rules pass for each operand,
* using a given operand count strategy.
Expand Down Expand Up @@ -394,6 +406,13 @@ public static SqlOperandTypeChecker variadic(
// Second operand optional (operand index 0, 1)
number -> number == 1);

public static final SqlOperandTypeChecker NUMERIC_INT32 =
sequence(
(operator, name) -> operator.getName() + "(<NUMERIC>, <INTEGER>)",
family(SqlTypeFamily.NUMERIC),
// Only 32-bit integer allowed for the second argument
new TypeNameChecker(SqlTypeName.INTEGER));

public static final SqlSingleOperandTypeChecker NUMERIC_CHARACTER =
family(SqlTypeFamily.NUMERIC, SqlTypeFamily.CHARACTER);

Expand Down
50 changes: 40 additions & 10 deletions testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1730,8 +1730,7 @@ void testCastToBoolean(CastType castType, SqlOperatorFixture f) {
f.checkScalarApprox("{fn RAND(42)}", "DOUBLE NOT NULL",
isWithin(0.63708, 0.001));
f.checkScalar("{fn ROUND(1251, -2)}", 1300, "INTEGER NOT NULL");
f.checkFails("^{fn ROUND(1251)}^", "Cannot apply '\\{fn ROUND\\}' to "
+ "arguments of type '\\{fn ROUND\\}\\(<INTEGER>\\)'.*", false);
f.checkScalar("{fn ROUND(1251)}", 1251, "INTEGER NOT NULL");
f.checkScalar("{fn SIGN(-1)}", -1, "INTEGER NOT NULL");
f.checkScalarApprox("{fn SIN(0.2)}", "DOUBLE NOT NULL",
isWithin(0.19867, 0.001));
Expand Down Expand Up @@ -7954,9 +7953,9 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
"INTEGER");
f.enableTypeCoercion(false)
.checkFails("^round('abc', 'def')^",
"Cannot apply 'ROUND' to arguments of type "
+ "'ROUND\\(<CHAR\\(3\\)>, <CHAR\\(3\\)>\\)'\\. Supported "
+ "form\\(s\\): 'ROUND\\(<NUMERIC>, <INTEGER>\\)'",
"Cannot apply 'ROUND' to arguments of type 'ROUND\\(<CHAR\\(3\\)>, <CHAR\\(3\\)>\\)'\\."
+ " Supported form\\(s\\): 'ROUND\\(<NUMERIC>\\)'\n"
+ "ROUND\\(<NUMERIC>, <INTEGER>\\)",
false);
f.checkType("round('abc', 'def')", "DECIMAL(19, 9) NOT NULL");
f.checkScalar("round(42, -1)", 40, "INTEGER NOT NULL");
Expand All @@ -7978,6 +7977,36 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
BigDecimal.valueOf(43, 0), "DECIMAL(5, 3) NOT NULL");
}

/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-6238">
* [CALCITE-6238] Exception while evaluating ROUND/TRUNCATE functions</a>. */
@Test void testRoundFail() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ROUND, VmName.EXPAND);
f.checkFails("^round(42, CAST(2 as BIGINT))^",
"Cannot apply 'ROUND' to arguments of type 'ROUND\\(<INTEGER>, <BIGINT>\\)'\\. "
+ "Supported form\\(s\\): 'ROUND\\(<NUMERIC>\\)'\nROUND\\(<NUMERIC>, <INTEGER>\\)",
false);
}

/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-6238">
* [CALCITE-6238] Exception while evaluating ROUND/TRUNCATE functions</a>. */
@Test void testTruncFail() {
SqlOperatorFixture f = fixture();
f = f.setFor(SqlStdOperatorTable.TRUNCATE, VmName.EXPAND)
.setFor(SqlLibraryOperators.TRUNC)
.withLibrary(SqlLibrary.BIG_QUERY);
f.checkFails("^truncate(42, CAST(2 as BIGINT))^",
"Cannot apply 'TRUNCATE' to arguments of type 'TRUNCATE\\(<INTEGER>, <BIGINT>\\)'\\. "
+ "Supported form\\(s\\): 'TRUNCATE\\(<NUMERIC>\\)'\n"
+ "TRUNCATE\\(<NUMERIC>, <INTEGER>\\)",
false);
f.checkFails("^trunc(42, CAST(2 as BIGINT))^",
"Cannot apply 'TRUNC' to arguments of type 'TRUNC\\(<INTEGER>, <BIGINT>\\)'\\. "
+ "Supported form\\(s\\): 'TRUNC\\(<NUMERIC>\\)'\nTRUNC\\(<NUMERIC>, <INTEGER>\\)",
false);
}


@Test void testSignFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.SIGN, VmName.EXPAND);
Expand Down Expand Up @@ -8111,9 +8140,9 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
"INTEGER");
f.enableTypeCoercion(false)
.checkFails("^trunc('abc', 'def')^",
"Cannot apply 'TRUNC' to arguments of type "
+ "'TRUNC\\(<CHAR\\(3\\)>, <CHAR\\(3\\)>\\)'\\. Supported "
+ "form\\(s\\): 'TRUNC\\(<NUMERIC>, <INTEGER>\\)'",
"Cannot apply 'TRUNC' to arguments of type 'TRUNC\\(<CHAR\\(3\\)>, <CHAR\\(3\\)>\\)'\\."
+ " Supported form\\(s\\): 'TRUNC\\(<NUMERIC>\\)'\n"
+ "TRUNC\\(<NUMERIC>, <INTEGER>\\)",
false);
f.checkType("trunc('abc', 'def')", "DECIMAL(19, 9) NOT NULL");
f.checkScalar("trunc(42, -1)", 40, "INTEGER NOT NULL");
Expand Down Expand Up @@ -8146,8 +8175,9 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
f.enableTypeCoercion(false)
.checkFails("^truncate('abc', 'def')^",
"Cannot apply 'TRUNCATE' to arguments of type "
+ "'TRUNCATE\\(<CHAR\\(3\\)>, <CHAR\\(3\\)>\\)'\\. Supported "
+ "form\\(s\\): 'TRUNCATE\\(<NUMERIC>, <INTEGER>\\)'",
+ "'TRUNCATE\\(<CHAR\\(3\\)>, <CHAR\\(3\\)>\\)'\\. Supported form\\(s\\):"
+ " 'TRUNCATE\\(<NUMERIC>\\)'\n"
+ "TRUNCATE\\(<NUMERIC>, <INTEGER>\\)",
false);
f.checkType("truncate('abc', 'def')", "DECIMAL(19, 9) NOT NULL");
f.checkScalar("truncate(42, -1)", 40, "INTEGER NOT NULL");
Expand Down

0 comments on commit 2e384ed

Please sign in to comment.