diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index 54efce3b8521..cd99de7b4a9a 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -22,16 +22,21 @@
import org.apache.calcite.rel.type.RelDataTypeImpl;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.rex.RexCallBinding;
+import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.sql.ExplicitOperatorBinding;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperatorBinding;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.validate.SqlValidatorNamespace;
import org.apache.calcite.util.Glossary;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.SqlNodeUtils;
import org.apache.calcite.util.Util;
import org.apache.kylin.guava30.shaded.common.base.Preconditions;
@@ -54,19 +59,23 @@ public abstract class ReturnTypes {
private ReturnTypes() {
}
- /** Creates a return-type inference that applies a rule then a sequence of
+ /**
+ * Creates a return-type inference that applies a rule then a sequence of
* rules, returning the first non-null result.
*
- * @see SqlReturnTypeInference#orElse(SqlReturnTypeInference) */
+ * @see SqlReturnTypeInference#orElse(SqlReturnTypeInference)
+ */
public static SqlReturnTypeInferenceChain chain(
SqlReturnTypeInference... rules) {
return new SqlReturnTypeInferenceChain(rules);
}
- /** Creates a return-type inference that applies a rule then a sequence of
+ /**
+ * Creates a return-type inference that applies a rule then a sequence of
* transforms.
*
- * @see SqlReturnTypeInference#andThen(SqlTypeTransform) */
+ * @see SqlReturnTypeInference#andThen(SqlTypeTransform)
+ */
public static SqlTypeTransformCascade cascade(SqlReturnTypeInference rule,
SqlTypeTransform... transforms) {
return new SqlTypeTransformCascade(rule, transforms);
@@ -101,10 +110,12 @@ public static ExplicitReturnTypeInference explicit(SqlTypeName typeName,
return explicit(RelDataTypeImpl.proto(typeName, precision, false));
}
- /** Returns a return-type inference that first transforms a binding and
+ /**
+ * Returns a return-type inference that first transforms a binding and
* then applies an inference.
*
- *
{@link #stripOrderBy} is an example of {@code bindingTransform}. */
+ *
{@link #stripOrderBy} is an example of {@code bindingTransform}.
+ */
public static SqlReturnTypeInference andThen(
UnaryOperator bindingTransform,
SqlReturnTypeInference typeInference) {
@@ -112,10 +123,12 @@ public static SqlReturnTypeInference andThen(
typeInference.inferReturnType(bindingTransform.apply(opBinding));
}
- /** Converts a binding of {@code FOO(x, y ORDER BY z)}
+ /**
+ * Converts a binding of {@code FOO(x, y ORDER BY z)}
* or {@code FOO(x, y ORDER BY z SEPARATOR s)}
* to a binding of {@code FOO(x, y)}.
- * Used for {@code STRING_AGG} and {@code GROUP_CONCAT}. */
+ * Used for {@code STRING_AGG} and {@code GROUP_CONCAT}.
+ */
public static SqlOperatorBinding stripOrderBy(
SqlOperatorBinding operatorBinding) {
if (operatorBinding instanceof SqlCallBinding) {
@@ -367,7 +380,7 @@ public static SqlCall stripSeparator(SqlCall call) {
* Type-inference strategy whereby the result type of a call is a Char.
*/
public static final SqlReturnTypeInference CHAR =
- explicit(SqlTypeName.CHAR);
+ explicit(SqlTypeName.CHAR);
/**
* Type-inference strategy whereby the result type of a call is a nullable
@@ -686,11 +699,69 @@ public static SqlCall stripSeparator(SqlCall call) {
*/
public static final SqlReturnTypeInference DECIMAL_PRODUCT = opBinding -> {
RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
- RelDataType type1 = opBinding.getOperandType(0);
- RelDataType type2 = opBinding.getOperandType(1);
- return typeFactory.getTypeSystem().deriveDecimalMultiplyType(typeFactory, type1, type2);
+ Pair result = getDecimalMultiplyBindingType(opBinding, typeFactory);
+ return typeFactory.getTypeSystem().deriveDecimalMultiplyType(typeFactory, result.left,
+ result.right);
};
+ private static Pair getDecimalMultiplyBindingType(SqlOperatorBinding opBinding,
+ RelDataTypeFactory typeFactory) {
+ if (opBinding instanceof SqlCallBinding) {
+ RelDataType type1 = createDecimalTypeOrDefault(typeFactory, (SqlCallBinding) opBinding, 0);
+ RelDataType type2 = createDecimalTypeOrDefault(typeFactory, (SqlCallBinding) opBinding, 1);
+ return Pair.of(type1, type2);
+ }
+ if (opBinding instanceof RexCallBinding) {
+ RelDataType type1 = createDecimalTypeOrDefault(typeFactory, (RexCallBinding) opBinding, 0);
+ RelDataType type2 = createDecimalTypeOrDefault(typeFactory, (RexCallBinding) opBinding, 1);
+ return Pair.of(type1, type2);
+ }
+ return Pair.of(opBinding.getOperandType(0), opBinding.getOperandType(1));
+ }
+
+ private static RelDataType createDecimalTypeOrDefault(RelDataTypeFactory typeFactory,
+ RexCallBinding opBinding, int ordinal) {
+ RelDataType defaultType = opBinding.getOperandType(ordinal);
+ try {
+ if (!SqlNodeUtils.isNumericLiteral(opBinding, ordinal)) {
+ return defaultType;
+ }
+
+ RexLiteral literal = (RexLiteral) opBinding.operands().get(ordinal);
+
+ if (SqlNodeUtils.isDecimalConstant(literal)) {
+ return defaultType;
+ }
+
+ Long value = literal.getValueAs(Long.class);
+ int length = (int) Math.floor(Math.log10(Math.abs(value))) + 1;
+ return typeFactory.createSqlType(SqlTypeName.DECIMAL, length, 0);
+ } catch (IllegalArgumentException | AssertionError e) {
+ return defaultType;
+ }
+ }
+
+ private static RelDataType createDecimalTypeOrDefault(RelDataTypeFactory typeFactory,
+ SqlCallBinding opBinding, int ordinal) {
+ RelDataType defaultType = opBinding.getOperandType(ordinal);
+ try {
+ if (!SqlNodeUtils.isNumericLiteral(opBinding, ordinal)) {
+ return defaultType;
+ }
+
+ SqlNumericLiteral literal =
+ (SqlNumericLiteral) opBinding.getCall().getOperandList().get(ordinal);
+ // When parsing into a **SqlNode**, integers are converted to **DECIMAL** type.
+ if (SqlNodeUtils.isDecimalConstant(literal)) {
+ return typeFactory.createSqlType(SqlTypeName.DECIMAL, literal.getPrec(),
+ literal.getScale());
+ }
+ return defaultType;
+ } catch (IllegalArgumentException e) {
+ return defaultType;
+ }
+ }
+
/**
* Same as {@link #DECIMAL_PRODUCT} but returns with nullability if any of
* the operands is nullable by using
@@ -746,7 +817,7 @@ public static SqlCall stripSeparator(SqlCall call) {
* {@link org.apache.calcite.sql.type.SqlTypeTransforms#TO_NULLABLE}.
*/
public static final SqlReturnTypeInference DOUBLE_QUOTIENT_NULLABLE =
- DOUBLE_QUOTIENT.andThen(SqlTypeTransforms.TO_NULLABLE);
+ DOUBLE_QUOTIENT.andThen(SqlTypeTransforms.TO_NULLABLE);
/**
* Type-inference strategy whereby the result type of a call is
@@ -933,7 +1004,7 @@ public static SqlCall stripSeparator(SqlCall call) {
List operandTypes = opBinding.collectOperandTypes();
final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
final RelDataTypeSystem typeSystem = typeFactory.getTypeSystem();
- for (RelDataType operandType: operandTypes) {
+ for (RelDataType operandType : operandTypes) {
int operandPrecision = operandType.getPrecision();
amount = (long) operandPrecision + amount;
if (operandPrecision == RelDataType.PRECISION_NOT_SPECIFIED) {
diff --git a/core/src/main/java/org/apache/calcite/util/SqlNodeUtils.java b/core/src/main/java/org/apache/calcite/util/SqlNodeUtils.java
new file mode 100644
index 000000000000..935c3b7246b4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/SqlNodeUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.util;
+
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.sql.SqlNumericLiteral;
+import org.apache.calcite.sql.SqlOperatorBinding;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
+
+public class SqlNodeUtils {
+
+ private SqlNodeUtils() {
+ }
+
+ public static boolean isDecimalConstant(SqlNumericLiteral literal) {
+ SqlTypeName typeName = literal.getTypeName();
+
+ return typeName == SqlTypeName.DECIMAL;
+ }
+
+ public static boolean isNumericLiteral(SqlOperatorBinding binding, int ordinal) {
+ return binding.isOperandLiteral(ordinal, false) &&
+ SqlTypeUtil.isNumeric(binding.getOperandType(ordinal));
+ }
+
+ public static boolean isDecimalConstant(RexLiteral literal) {
+ SqlTypeName typeName = literal.getType().getSqlTypeName();
+
+ return typeName == SqlTypeName.DECIMAL
+ || typeName == SqlTypeName.DOUBLE
+ || typeName == SqlTypeName.REAL
+ || typeName == SqlTypeName.FLOAT;
+ }
+}
diff --git a/gradle.properties b/gradle.properties
index 413426bef7d1..354bf93f31ff 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -27,7 +27,7 @@ systemProp.org.gradle.internal.publish.checksums.insecure=true
# This is version for Calcite itself
# Note: it should not include "-SNAPSHOT" as it is automatically added by build.gradle.kts
# Release version can be generated by using -Prelease or -Prc= arguments
-calcite.version=1.30.0-kylin-5.x-r5
+calcite.version=1.30.0-kylin-5.x-r6
# This is a version to be used from Maven repository. It can be overridden by localAvatica below
calcite.avatica.version=1.22.0