|
24 | 24 | import org.apache.calcite.rel.type.RelProtoDataType; |
25 | 25 | import org.apache.calcite.rex.RexCallBinding; |
26 | 26 | import org.apache.calcite.rex.RexLiteral; |
| 27 | +import org.apache.calcite.rex.RexNode; |
27 | 28 | import org.apache.calcite.sql.ExplicitOperatorBinding; |
28 | 29 | import org.apache.calcite.sql.SqlCall; |
29 | 30 | import org.apache.calcite.sql.SqlCallBinding; |
30 | 31 | import org.apache.calcite.sql.SqlCollation; |
31 | 32 | import org.apache.calcite.sql.SqlKind; |
| 33 | +import org.apache.calcite.sql.SqlNode; |
32 | 34 | import org.apache.calcite.sql.SqlNodeList; |
33 | 35 | import org.apache.calcite.sql.SqlNumericLiteral; |
34 | 36 | import org.apache.calcite.sql.SqlOperatorBinding; |
@@ -234,8 +236,7 @@ public static SqlCall stripSeparator(SqlCall call) { |
234 | 236 | */ |
235 | 237 | public static final SqlReturnTypeInference ARG0_NULLABLE_IF_EMPTY = |
236 | 238 | new OrdinalReturnTypeInference(0) { |
237 | | - @Override public RelDataType |
238 | | - inferReturnType(SqlOperatorBinding opBinding) { |
| 239 | + @Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) { |
239 | 240 | final RelDataType type = super.inferReturnType(opBinding); |
240 | 241 | if (opBinding.getGroupCount() == 0 || opBinding.hasFilter()) { |
241 | 242 | return opBinding.getTypeFactory() |
@@ -704,62 +705,290 @@ public static SqlCall stripSeparator(SqlCall call) { |
704 | 705 | result.right); |
705 | 706 | }; |
706 | 707 |
|
707 | | - private static Pair<RelDataType, RelDataType> getDecimalMultiplyBindingType(SqlOperatorBinding opBinding, |
708 | | - RelDataTypeFactory typeFactory) { |
| 708 | + /** |
| 709 | + * Determines the appropriate data types for decimal multiplication operations. |
| 710 | + * |
| 711 | + * <p>This method serves as a dispatcher that routes to the appropriate handler |
| 712 | + * based on the type of operator binding. It handles both SQL parse tree bindings |
| 713 | + * (SqlCallBinding) and relational expression bindings (RexCallBinding), with a |
| 714 | + * fallback for other binding types.</p> |
| 715 | + * |
| 716 | + * <p>The method is crucial for decimal multiplication type inference because it |
| 717 | + * needs to analyze the actual operand values (not just their declared types) to |
| 718 | + * determine if integer literals should be converted to decimal types for proper |
| 719 | + * decimal arithmetic.</p> |
| 720 | + * |
| 721 | + * @param opBinding the operator binding containing operand information and types |
| 722 | + * @param typeFactory the type factory used to create new data types if needed |
| 723 | + * @return a Pair containing the left and right operand types to be used for |
| 724 | + * decimal multiplication, possibly with type conversions applied |
| 725 | + */ |
| 726 | + private static Pair<RelDataType, RelDataType> getDecimalMultiplyBindingType( |
| 727 | + SqlOperatorBinding opBinding, RelDataTypeFactory typeFactory) { |
| 728 | + // Route to SqlCallBinding handler for SQL parse tree scenarios |
709 | 729 | if (opBinding instanceof SqlCallBinding) { |
710 | | - RelDataType type1 = createDecimalTypeOrDefault(typeFactory, (SqlCallBinding) opBinding, 0); |
711 | | - RelDataType type2 = createDecimalTypeOrDefault(typeFactory, (SqlCallBinding) opBinding, 1); |
712 | | - return Pair.of(type1, type2); |
| 730 | + return getRelDataTypeRelDataTypePair((SqlCallBinding) opBinding, typeFactory); |
713 | 731 | } |
| 732 | + // Route to RexCallBinding handler for relational expression scenarios |
714 | 733 | if (opBinding instanceof RexCallBinding) { |
715 | | - RelDataType type1 = createDecimalTypeOrDefault(typeFactory, (RexCallBinding) opBinding, 0); |
716 | | - RelDataType type2 = createDecimalTypeOrDefault(typeFactory, (RexCallBinding) opBinding, 1); |
717 | | - return Pair.of(type1, type2); |
| 734 | + return getRelDataTypeRelDataTypePair((RexCallBinding) opBinding, typeFactory); |
718 | 735 | } |
| 736 | + // Fallback: return original operand types for other binding types |
719 | 737 | return Pair.of(opBinding.getOperandType(0), opBinding.getOperandType(1)); |
720 | 738 | } |
721 | 739 |
|
722 | | - private static RelDataType createDecimalTypeOrDefault(RelDataTypeFactory typeFactory, |
723 | | - RexCallBinding opBinding, int ordinal) { |
724 | | - RelDataType defaultType = opBinding.getOperandType(ordinal); |
| 740 | + /** |
| 741 | + * Determines operand types for decimal multiplication in RexCallBinding scenarios. |
| 742 | + * |
| 743 | + * <p>This method handles relational expression bindings (RexCallBinding) where operands |
| 744 | + * are represented as RexNode objects. It analyzes both the declared types and actual |
| 745 | + * values to determine if type conversions are needed for proper decimal arithmetic.</p> |
| 746 | + * |
| 747 | + * <p>The method implements a comprehensive type inference strategy that considers: |
| 748 | + * <ul> |
| 749 | + * <li>Whether operands have decimal types or contain decimal constants</li> |
| 750 | + * <li>Whether operands are numeric (including integer literals)</li> |
| 751 | + * <li>Whether integer literals should be converted to decimal types</li> |
| 752 | + * </ul> |
| 753 | + * </p> |
| 754 | + * |
| 755 | + * <p>Key scenarios handled: |
| 756 | + * <ul> |
| 757 | + * <li>Decimal × Decimal: both operands maintain decimal types</li> |
| 758 | + * <li>Decimal × Integer: integer may be converted to decimal</li> |
| 759 | + * <li>Integer × Decimal: integer may be converted to decimal</li> |
| 760 | + * <li>Integer × Integer: no conversion, return original types</li> |
| 761 | + * </ul> |
| 762 | + * </p> |
| 763 | + * |
| 764 | + * @param opBinding the RexCallBinding containing RexNode operands |
| 765 | + * @param typeFactory the type factory for creating new data types |
| 766 | + * @return a Pair containing the potentially converted left and right operand types |
| 767 | + */ |
| 768 | + private static Pair<RelDataType, RelDataType> getRelDataTypeRelDataTypePair( |
| 769 | + RexCallBinding opBinding, RelDataTypeFactory typeFactory) { |
| 770 | + // Get the default types for both operands |
| 771 | + RelDataType leftDefaultType = opBinding.getOperandType(0); |
| 772 | + RelDataType rightDefaultType = opBinding.getOperandType(1); |
725 | 773 | try { |
726 | | - if (!SqlNodeUtils.isNumericLiteral(opBinding, ordinal)) { |
727 | | - return defaultType; |
| 774 | + // Extract the actual RexNode operands for value analysis |
| 775 | + RexNode leftOperand = opBinding.operands().get(0); |
| 776 | + RexNode rightOperand = opBinding.operands().get(1); |
| 777 | + |
| 778 | + // Determine if operands are decimal (either by type or by constant value) |
| 779 | + // Check if left operand is decimal by type or contains decimal constant |
| 780 | + boolean leftIsDecimal = SqlTypeUtil.isDecimal(leftDefaultType) |
| 781 | + || SqlNodeUtils.isDecimalConstantRexNode(leftOperand); |
| 782 | + // Check if right operand is decimal by type or contains decimal constant |
| 783 | + boolean rightIsDecimal = SqlTypeUtil.isDecimal(rightDefaultType) |
| 784 | + || SqlNodeUtils.isDecimalConstantRexNode(rightOperand); |
| 785 | + // Determine if operands are numeric (either by type or by literal value) |
| 786 | + boolean leftIsNumeric = SqlTypeUtil.isNumeric(leftDefaultType) |
| 787 | + || SqlNodeUtils.isNumericLiteralRexNode(leftOperand); |
| 788 | + boolean rightIsNumeric = SqlTypeUtil.isNumeric(rightDefaultType) |
| 789 | + || SqlNodeUtils.isNumericLiteralRexNode(rightOperand); |
| 790 | + |
| 791 | + // Apply decimal type inference if at least one operand is decimal and both are numeric |
| 792 | + // This covers scenarios where we need decimal arithmetic precision |
| 793 | + if ((leftIsDecimal || rightIsDecimal) && leftIsNumeric && rightIsNumeric) { |
| 794 | + // Convert operands to appropriate decimal types if needed |
| 795 | + RelDataType type1 = createDecimalTypeOrDefault(typeFactory, opBinding, 0, leftDefaultType); |
| 796 | + RelDataType type2 = createDecimalTypeOrDefault(typeFactory, opBinding, 1, rightDefaultType); |
| 797 | + return Pair.of(type1, type2); |
728 | 798 | } |
729 | 799 |
|
730 | | - RexLiteral literal = (RexLiteral) opBinding.operands().get(ordinal); |
| 800 | + // Fallback: both operands are numeric but neither is decimal, no conversion needed |
| 801 | + return Pair.of(leftDefaultType, rightDefaultType); |
| 802 | + } catch (Exception e) { |
| 803 | + // Exception safety: return original types if any error occurs during analysis |
| 804 | + return Pair.of(leftDefaultType, rightDefaultType); |
| 805 | + } |
| 806 | + } |
731 | 807 |
|
732 | | - if (SqlNodeUtils.isDecimalConstant(literal)) { |
| 808 | + /** |
| 809 | + * Converts integer literals to decimal types for RexCallBinding scenarios. |
| 810 | + * |
| 811 | + * <p>This method is responsible for type conversion in relational expression scenarios |
| 812 | + * where integer literals need to be promoted to decimal types for proper decimal arithmetic. |
| 813 | + * The conversion is essential when multiplying integers with decimals to maintain |
| 814 | + * precision and avoid unintended integer arithmetic.</p> |
| 815 | + * |
| 816 | + * <p>Conversion logic: |
| 817 | + * <ul> |
| 818 | + * <li>If the operand is already a decimal constant, return the default type</li> |
| 819 | + * <li>If the operand is an integer literal, convert it to DECIMAL with appropriate |
| 820 | + * precision</li> |
| 821 | + * <li>For other cases, return the default type unchanged</li> |
| 822 | + * </ul> |
| 823 | + * </p> |
| 824 | + * |
| 825 | + * <p>The precision calculation for converted integers uses the number of digits |
| 826 | + * in the integer value. For example: |
| 827 | + * <ul> |
| 828 | + * <li>123 → DECIMAL(3, 0) (3 digits)</li> |
| 829 | + * <li>45 → DECIMAL(2, 0) (2 digits)</li> |
| 830 | + * <li>0 → DECIMAL(1, 0) (special case)</li> |
| 831 | + * </ul> |
| 832 | + * </p> |
| 833 | + * |
| 834 | + * @param typeFactory the type factory for creating new DECIMAL types |
| 835 | + * @param opBinding the RexCallBinding containing the operands |
| 836 | + * @param ordinal the zero-based index of the operand to analyze |
| 837 | + * @param defaultType the default type to return if no conversion is needed |
| 838 | + * @return either a converted DECIMAL type or the original default type |
| 839 | + */ |
| 840 | + private static RelDataType createDecimalTypeOrDefault(RelDataTypeFactory typeFactory, |
| 841 | + RexCallBinding opBinding, int ordinal, RelDataType defaultType) { |
| 842 | + // Check if the operand at the specified position is a numeric literal |
| 843 | + if (SqlNodeUtils.isNumericLiteral(opBinding, ordinal)) { |
| 844 | + RexNode node = opBinding.operands().get(ordinal); |
| 845 | + // If it's already a decimal constant, no conversion needed |
| 846 | + if (SqlNodeUtils.isDecimalConstant(node)) { |
733 | 847 | return defaultType; |
734 | 848 | } |
735 | 849 |
|
736 | | - Long value = literal.getValueAs(Long.class); |
737 | | - int length = (int) Math.floor(Math.log10(Math.abs(value))) + 1; |
738 | | - return typeFactory.createSqlType(SqlTypeName.DECIMAL, length, 0); |
739 | | - } catch (IllegalArgumentException | AssertionError e) { |
740 | | - return defaultType; |
| 850 | + // Attempt to convert integer literals to decimal for proper decimal multiplication |
| 851 | + RexLiteral literal = (RexLiteral) node; |
| 852 | + RelDataType type = literal.getType(); |
| 853 | + // Check if it's an exact numeric type (INTEGER, BIGINT, etc.) but not already DECIMAL |
| 854 | + if (SqlTypeUtil.isExactNumeric(type) && !SqlTypeUtil.isDecimal(type)) { |
| 855 | + // Convert integer types (INTEGER, BIGINT, etc.) to DECIMAL |
| 856 | + Long value = literal.getValueAs(Long.class); |
| 857 | + if (value != null) { |
| 858 | + // Calculate precision based on the number of digits in the integer |
| 859 | + // For example: 123 → 3 digits, 45 → 2 digits, 0 → 1 digit, -123 → 4 digits |
| 860 | + int length; |
| 861 | + if (value == 0) { |
| 862 | + // Special case: 0 should have precision 1 |
| 863 | + length = 1; |
| 864 | + } else if (value < 0) { |
| 865 | + // like sqlNode, negative number add 1 for '-' |
| 866 | + length = (int) Math.floor(Math.log10(Math.abs(value))) + 2; |
| 867 | + } else { |
| 868 | + length = (int) Math.floor(Math.log10(value)) + 1; |
| 869 | + } |
| 870 | + // Create DECIMAL type with calculated precision and 0 scale |
| 871 | + return typeFactory.createSqlType(SqlTypeName.DECIMAL, length, 0); |
| 872 | + } |
| 873 | + } |
741 | 874 | } |
| 875 | + // Return default type for non-literals or when conversion is not applicable |
| 876 | + return defaultType; |
742 | 877 | } |
743 | 878 |
|
744 | | - private static RelDataType createDecimalTypeOrDefault(RelDataTypeFactory typeFactory, |
745 | | - SqlCallBinding opBinding, int ordinal) { |
746 | | - RelDataType defaultType = opBinding.getOperandType(ordinal); |
| 879 | + /** |
| 880 | + * Determines operand types for decimal multiplication in SqlCallBinding scenarios. |
| 881 | + * |
| 882 | + * <p>This method handles SQL parse tree bindings (SqlCallBinding) where operands |
| 883 | + * are represented as SqlNode objects. It analyzes both the declared types and actual |
| 884 | + * values to determine if type conversions are needed for proper decimal arithmetic.</p> |
| 885 | + * |
| 886 | + * <p>Similar to the RexCallBinding version, this method implements comprehensive |
| 887 | + * type inference but operates on SqlNode objects instead of RexNode objects. |
| 888 | + * The key difference is that during SQL parsing, integer literals are automatically |
| 889 | + * converted to DECIMAL type, which affects the conversion logic.</p> |
| 890 | + * |
| 891 | + * <p>Key scenarios handled: |
| 892 | + * <ul> |
| 893 | + * <li>Decimal × Decimal: both operands maintain decimal types</li> |
| 894 | + * <li>Decimal × Integer: integer may be converted to decimal</li> |
| 895 | + * <li>Integer × Decimal: integer may be converted to decimal</li> |
| 896 | + * <li>Integer × Integer: no conversion, return original types</li> |
| 897 | + * </ul> |
| 898 | + * </p> |
| 899 | + * |
| 900 | + * @param opBinding the SqlCallBinding containing SqlNode operands |
| 901 | + * @param typeFactory the type factory for creating new data types |
| 902 | + * @return a Pair containing the potentially converted left and right operand types |
| 903 | + */ |
| 904 | + private static Pair<RelDataType, RelDataType> getRelDataTypeRelDataTypePair( |
| 905 | + SqlCallBinding opBinding, RelDataTypeFactory typeFactory) { |
| 906 | + // Get the default types for both operands |
| 907 | + RelDataType leftDefaultType = opBinding.getOperandType(0); |
| 908 | + RelDataType rightDefaultType = opBinding.getOperandType(1); |
747 | 909 | try { |
748 | | - if (!SqlNodeUtils.isNumericLiteral(opBinding, ordinal)) { |
749 | | - return defaultType; |
| 910 | + // Extract the actual SqlNode operands for value analysis |
| 911 | + SqlNode leftOperand = opBinding.operands().get(0); |
| 912 | + SqlNode rightOperand = opBinding.operands().get(1); |
| 913 | + |
| 914 | + // Determine if operands are decimal (either by type or by constant value) |
| 915 | + // Check if left operand is decimal by type or contains decimal constant |
| 916 | + boolean leftIsDecimal = SqlTypeUtil.isDecimal(leftDefaultType) |
| 917 | + || SqlNodeUtils.isDecimalConstantSqlNode(leftOperand); |
| 918 | + // Check if right operand is decimal by type or contains decimal constant |
| 919 | + boolean rightIsDecimal = SqlTypeUtil.isDecimal(rightDefaultType) |
| 920 | + || SqlNodeUtils.isDecimalConstantSqlNode(rightOperand); |
| 921 | + // Determine if operands are numeric (either by type or by literal value) |
| 922 | + boolean leftIsNumeric = SqlTypeUtil.isNumeric(leftDefaultType) |
| 923 | + || SqlNodeUtils.isNumericLiteralSqlNode(leftOperand); |
| 924 | + boolean rightIsNumeric = SqlTypeUtil.isNumeric(rightDefaultType) |
| 925 | + || SqlNodeUtils.isNumericLiteralSqlNode(rightOperand); |
| 926 | + |
| 927 | + // Apply decimal type inference if at least one operand is decimal and both are numeric |
| 928 | + // This covers scenarios where we need decimal arithmetic precision |
| 929 | + if ((leftIsDecimal || rightIsDecimal) && leftIsNumeric && rightIsNumeric) { |
| 930 | + // Convert operands to appropriate decimal types if needed |
| 931 | + RelDataType type1 = createDecimalTypeOrDefault(typeFactory, opBinding, 0, leftDefaultType); |
| 932 | + RelDataType type2 = createDecimalTypeOrDefault(typeFactory, opBinding, 1, rightDefaultType); |
| 933 | + return Pair.of(type1, type2); |
750 | 934 | } |
751 | 935 |
|
| 936 | + // Return default types for scenarios where both operands are numeric but neither is decimal |
| 937 | + return Pair.of(leftDefaultType, rightDefaultType); |
| 938 | + } catch (Exception e) { |
| 939 | + // Exception safety: return original types if any error occurs during analysis |
| 940 | + return Pair.of(leftDefaultType, rightDefaultType); |
| 941 | + } |
| 942 | + } |
| 943 | + |
| 944 | + /** |
| 945 | + * Converts numeric literals to decimal types for SqlCallBinding scenarios. |
| 946 | + * |
| 947 | + * <p>This method handles type conversion in SQL parse tree scenarios where numeric literals |
| 948 | + * need to be processed for decimal arithmetic. Unlike the RexCallBinding version, this method |
| 949 | + * operates on SqlNode objects where integer literals have already been converted to DECIMAL |
| 950 | + * type during SQL parsing.</p> |
| 951 | + * |
| 952 | + * <p>Key difference from RexCallBinding version: |
| 953 | + * <ul> |
| 954 | + * <li>During SQL parsing, integer literals are automatically converted to DECIMAL type</li> |
| 955 | + * <li>This method primarily ensures proper precision and scale are preserved</li> |
| 956 | + * <li>No need to manually calculate precision from integer values</li> |
| 957 | + * </ul> |
| 958 | + * </p> |
| 959 | + * |
| 960 | + * <p>Conversion logic: |
| 961 | + * <ul> |
| 962 | + * <li>If the operand is a numeric literal (DECIMAL or INTEGER), create DECIMAL type</li> |
| 963 | + * <li>Use the literal's existing precision and scale information</li> |
| 964 | + * <li>For non-literals, return the default type unchanged</li> |
| 965 | + * </ul> |
| 966 | + * </p> |
| 967 | + * |
| 968 | + * @param typeFactory the type factory for creating new DECIMAL types |
| 969 | + * @param opBinding the SqlCallBinding containing SqlNode operands |
| 970 | + * @param ordinal the zero-based index of the operand to analyze |
| 971 | + * @param defaultType the default type to return if no conversion is needed |
| 972 | + * @return either a converted DECIMAL type or the original default type |
| 973 | + */ |
| 974 | + private static RelDataType createDecimalTypeOrDefault(RelDataTypeFactory typeFactory, |
| 975 | + SqlCallBinding opBinding, int ordinal, RelDataType defaultType) { |
| 976 | + // Check if the operand at the specified position is a numeric literal |
| 977 | + if (SqlNodeUtils.isNumericLiteral(opBinding, ordinal)) { |
| 978 | + // Extract the SqlNumericLiteral from the call's operand list |
752 | 979 | SqlNumericLiteral literal = |
753 | 980 | (SqlNumericLiteral) opBinding.getCall().getOperandList().get(ordinal); |
754 | | - // When parsing into a **SqlNode**, integers are converted to **DECIMAL** type. |
755 | | - if (SqlNodeUtils.isDecimalConstant(literal)) { |
| 981 | + // When parsing into a SqlNode, integers are converted to DECIMAL type |
| 982 | + // This method ensures both decimal and integer literals are properly handled |
| 983 | + if (SqlNodeUtils.isDecimalOrIntegerConstant(literal)) { |
| 984 | + // Create DECIMAL type using the literal's existing precision and scale |
| 985 | + // This preserves the original precision/scale information from parsing |
756 | 986 | return typeFactory.createSqlType(SqlTypeName.DECIMAL, literal.getPrec(), |
757 | 987 | literal.getScale()); |
758 | 988 | } |
759 | | - return defaultType; |
760 | | - } catch (IllegalArgumentException e) { |
761 | | - return defaultType; |
762 | 989 | } |
| 990 | + // Return default type for non-literals or when conversion is not applicable |
| 991 | + return defaultType; |
763 | 992 | } |
764 | 993 |
|
765 | 994 | /** |
|
0 commit comments