diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java index 06a8dab24f8..ac14eabfc5d 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java @@ -1181,20 +1181,26 @@ private static RelDataType arrayAppendPrependReturnType(SqlOperatorBinding opBin final RelDataType arrayType = opBinding.collectOperandTypes().get(0); final RelDataType componentType = arrayType.getComponentType(); final RelDataType elementType = opBinding.collectOperandTypes().get(1); + requireNonNull(componentType, () -> "componentType of " + arrayType); + RelDataType type = opBinding.getTypeFactory().leastRestrictive( ImmutableList.of(componentType, elementType)); + requireNonNull(type, "inferred array element type"); + if (elementType.isNullable()) { type = opBinding.getTypeFactory().createTypeWithNullability(type, true); } - if (!componentType.isNullable() && !componentType.equalsSansFieldNames(elementType)) { - SqlValidatorUtil. - adjustTypeForArrayFunctionConstructor(type, opBinding, 0); + + // make explicit CAST for array elements and inserted element to the biggest type + // if array component type not equals to inserted element type + if (!componentType.equalsSansFieldNames(elementType)) { + // 0, 1 is the operand index to be CAST + // For array_append/array_prepend, 0 is the array arg and 1 is the inserted element SqlValidatorUtil. - adjustTypeForArrayFunctionConstructor(type, opBinding, 1); + adjustTypeForArrayOperationFunction(type, opBinding, 0, 1); } - requireNonNull(type, "inferred array element type"); return SqlTypeUtil.createArrayType(opBinding.getTypeFactory(), type, arrayType.isNullable()); } @@ -1270,23 +1276,27 @@ private static RelDataType arrayInsertReturnType(SqlOperatorBinding opBinding) { final RelDataType arrayType = opBinding.collectOperandTypes().get(0); final RelDataType componentType = arrayType.getComponentType(); final RelDataType elementType = opBinding.collectOperandTypes().get(2); + requireNonNull(componentType, () -> "componentType of " + arrayType); + // we don't need to do leastRestrictive on componentType and elementType, // because in operand checker we limit the elementType must equals array component type. // So we use componentType directly. RelDataType type = opBinding.getTypeFactory().leastRestrictive( ImmutableList.of(componentType, elementType)); + requireNonNull(type, "inferred array element type"); + if (elementType.isNullable()) { type = opBinding.getTypeFactory().createTypeWithNullability(type, true); } - - if (!componentType.isNullable() && !componentType.equalsSansFieldNames(elementType)) { - SqlValidatorUtil. - adjustTypeForArrayFunctionConstructor(type, opBinding, 0); + // make explicit CAST for array elements and inserted element to the biggest type + // if array component type not equals to inserted element type + if (!componentType.equalsSansFieldNames(elementType)) { + // 0, 2 is the operand index to be CAST + // For array_insert, 0 is the array arg and 2 is the inserted element SqlValidatorUtil. - adjustTypeForArrayFunctionConstructor(type, opBinding, 2); + adjustTypeForArrayOperationFunction(type, opBinding, 0, 2); } - requireNonNull(type, "inferred array element type"); return SqlTypeUtil.createArrayType(opBinding.getTypeFactory(), type, arrayType.isNullable()); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java index 9a36da34c3d..19984b1774c 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java @@ -1328,32 +1328,48 @@ public static void adjustTypeForArrayConstructor( } /** - * When the array element does not equal the biggest type, make explicit casting. + * Adjusts the types of specified operands in an array operation to match a given target type. + * This is particularly useful in the context of SQL operations involving array functions, + * where it's necessary to ensure that all operands have consistent types for the operation + * to be valid. * - * @param componentType derived array component type - * @param opBinding description of call - * @param index index of opBinding + *
This method operates on the assumption that the operands to be adjusted are part of a + * {@link SqlCall}, which is bound within a {@link SqlOperatorBinding}. The operands to be + * cast are identified by their indexes within the {@code operands} list of the {@link SqlCall}. + * The method performs a dynamic check to determine if an operand is a basic call to an array. + * If so, it casts each element within the array to the target type. + * Otherwise, it casts the operand itself to the target type. + * + *
Example usage: For an operation like {@code array_append(array(1,2), cast(2 as tinyint))},
+ * if targetType is double, this method would ensure that the elements of the
+ * first array and the second operand are cast to double.
+ *
+ * @param targetType The target {@link RelDataType} to which the operands should be cast.
+ * @param opBinding The {@link SqlOperatorBinding} context, which provides access to the
+ * {@link SqlCall} and its operands.
+ * @param indexes The indexes of the operands within the {@link SqlCall} that need to be
+ * adjusted to the target type.
+ * @throws NullPointerException if {@code targetType} is {@code null}.
*/
- public static void adjustTypeForArrayFunctionConstructor(
- RelDataType componentType, SqlOperatorBinding opBinding, int index) {
+ public static void adjustTypeForArrayOperationFunction(
+ RelDataType targetType, SqlOperatorBinding opBinding, int... indexes) {
if (opBinding instanceof SqlCallBinding) {
- requireNonNull(componentType, "array component type");
- adjustTypeForMultisetConstructor(
- componentType, (SqlCallBinding) opBinding, index);
- }
- }
-
- private static void adjustTypeForMultisetConstructor(
- RelDataType evenType, SqlCallBinding sqlCallBinding, int index) {
- SqlCall call = sqlCallBinding.getCall();
- List