diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 1cac3913ea84..7f358521573b 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -2332,6 +2332,8 @@ private static class BinaryImplementor extends AbstractRexCallImplementor {
final Primitive primitive = Primitive.ofBoxOr(type0);
if (primitive == null
|| type1 == BigDecimal.class
+ || backupMethodName.equals("plus")
+ && isNumberOrString(type0) && isNumberOrString(type1)
|| COMPARISON_OPERATORS.contains(op)
&& !COMP_OP_TYPES.contains(primitive)) {
return Expressions.call(SqlFunctions.class, backupMethodName,
@@ -2352,6 +2354,15 @@ private static class BinaryImplementor extends AbstractRexCallImplementor {
argValueList.get(0), argValueList.get(1));
}
+ // see https://olapio.atlassian.net/browse/KE-42243
+ private boolean isNumberOrString(Type type) {
+ if (type == String.class || type == BigDecimal.class) {
+ return true;
+ }
+ Primitive primitive = Primitive.ofBoxOr(type);
+ return primitive != null && Number.class.isAssignableFrom(primitive.boxClass);
+ }
+
/** Returns whether any of a call's operands have ANY type. */
private static boolean anyAnyOperands(RexCall call) {
for (RexNode operand : call.operands) {
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index c23aa06e04f4..cc0e54a73564 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -1006,30 +1006,35 @@ public static int plus(int b0, int b1) {
}
// see https://olapio.atlassian.net/browse/KE-42084
- public static Double plus(String s0, String s1) {
+ // see https://olapio.atlassian.net/browse/KE-42243
+ public static BigDecimal plus(String s0, String s1) {
try {
- return plus(toBigDecimal(s0), toBigDecimal(s1)).doubleValue();
+ return plus(toBigDecimal(s0), toBigDecimal(s1));
} catch (NumberFormatException ignored) {
return null;
}
}
- public static Double plus(String s0, BigDecimal b1) {
+ public static BigDecimal plus(String s0, BigDecimal b1) {
try {
- return plus(toBigDecimal(s0), b1).doubleValue();
+ return plus(toBigDecimal(s0), b1);
} catch (NumberFormatException ignored) {
return null;
}
}
- public static Double plus(String s0, Number b1) {
+ public static BigDecimal plus(String s0, Number b1) {
return plus(s0, toBigDecimal(b1));
}
- public static Double plus(Number b1, String s0) {
+ public static BigDecimal plus(Number b1, String s0) {
return plus(s0, toBigDecimal(b1));
}
+ public static BigDecimal plus(Number b1, Number b2) {
+ return plus(toBigDecimal(b1), toBigDecimal(b2));
+ }
+
/** SQL +
operator applied to Object values (at least one operand
* has ANY type; either may be null). */
public static @PolyNull Object plusAny(@PolyNull Object b0,
@@ -2213,7 +2218,8 @@ public static BigDecimal toBigDecimal(String s) {
public static BigDecimal toBigDecimal(Number number) {
// There are some values of "long" that cannot be represented as "double".
// Not so "int". If it isn't a long, go straight to double.
- return number instanceof BigDecimal ? (BigDecimal) number
+ return number == null ? null
+ : number instanceof BigDecimal ? (BigDecimal) number
: number instanceof BigInteger ? new BigDecimal((BigInteger) number)
: number instanceof Long ? new BigDecimal(number.longValue())
: new BigDecimal(number.doubleValue());
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
index ce562a9a5167..2a108524e3bf 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
@@ -173,6 +173,12 @@ protected boolean binaryArithmeticWithStrings(
SqlCallBinding binding,
RelDataType left,
RelDataType right) {
+ // see https://olapio.atlassian.net/browse/KE-42243
+ // Calcite's implicit conversion may fail when the operator is "plus" and the left and right
+ // sides are numeric and string types respectively, which will not be supported in Kylin,
+ // and the logic is commented out for now.
+ return false;
+
// For expression "NUMERIC CHARACTER",
// PostgreSQL and MS-SQL coerce the CHARACTER operand to NUMERIC,
// i.e. for '9':VARCHAR(1) / 2: INT, '9' would be coerced to INTEGER,
@@ -183,20 +189,20 @@ protected boolean binaryArithmeticWithStrings(
// Keep sync with PostgreSQL and MS-SQL because their behaviors are more in
// line with the SQL standard.
- if (SqlTypeUtil.isString(left) && SqlTypeUtil.isNumeric(right)) {
- // If the numeric operand is DECIMAL type, coerce the STRING operand to
- // max precision/scale DECIMAL.
- if (SqlTypeUtil.isDecimal(right)) {
- right = SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
- }
- return coerceOperandType(binding.getScope(), binding.getCall(), 0, right);
- } else if (SqlTypeUtil.isNumeric(left) && SqlTypeUtil.isString(right)) {
- if (SqlTypeUtil.isDecimal(left)) {
- left = SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
- }
- return coerceOperandType(binding.getScope(), binding.getCall(), 1, left);
- }
- return false;
+// if (SqlTypeUtil.isString(left) && SqlTypeUtil.isNumeric(right)) {
+// // If the numeric operand is DECIMAL type, coerce the STRING operand to
+// // max precision/scale DECIMAL.
+// if (SqlTypeUtil.isDecimal(right)) {
+// right = SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
+// }
+// return coerceOperandType(binding.getScope(), binding.getCall(), 0, right);
+// } else if (SqlTypeUtil.isNumeric(left) && SqlTypeUtil.isString(right)) {
+// if (SqlTypeUtil.isDecimal(left)) {
+// left = SqlTypeUtil.getMaxPrecisionScaleDecimal(factory);
+// }
+// return coerceOperandType(binding.getScope(), binding.getCall(), 1, left);
+// }
+// return false;
}
/**