This is an automated email from the ASF dual-hosted git repository.

taiyangli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-gluten.git


The following commit(s) were added to refs/heads/main by this push:
     new 66e816fb10 [GLUTEN-8343][CH]Fix cast number to decimal (#8351)
66e816fb10 is described below

commit 66e816fb10e821f5544a4e5cb296f456039b022f
Author: kevinyhzou <[email protected]>
AuthorDate: Fri Jan 10 09:39:00 2025 +0800

    [GLUTEN-8343][CH]Fix cast number to decimal (#8351)
    
    * fix cast number to decimal
    
    * simply code
    
    * fix ci
---
 .../GlutenClickHouseTPCHSaltNullParquetSuite.scala | 15 ++++
 .../SparkFunctionCheckDecimalOverflow.cpp          | 94 +++++++++++++++-------
 cpp-ch/local-engine/Parser/ExpressionParser.cpp    | 13 +--
 3 files changed, 88 insertions(+), 34 deletions(-)

diff --git 
a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
 
b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
index 1ebdded5e1..dc01cf7c40 100644
--- 
a/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
+++ 
b/backends-clickhouse/src/test/scala/org/apache/gluten/execution/tpch/GlutenClickHouseTPCHSaltNullParquetSuite.scala
@@ -3362,5 +3362,20 @@ class GlutenClickHouseTPCHSaltNullParquetSuite extends 
GlutenClickHouseTPCHAbstr
     val sql = "select * from test_filter where (c1, c2) in (('a1', 'b1'), 
('a2', 'b2'))"
     compareResultsAgainstVanillaSpark(sql, true, { _ => })
   }
+
+  test("GLUTEN-8343: Cast number to decimal") {
+    val create_table_sql = "create table test_tbl_8343(id bigint, d bigint, f 
double) using parquet"
+    val insert_data_sql =
+      "insert into test_tbl_8343 values(1, 55, 55.12345), (2, 137438953483, 
137438953483.12345), (3, -12, -12.123), (4, 0, 0.0001), (5, NULL, NULL), (6, 
%d, NULL), (7, %d, NULL)"
+        .format(Double.MaxValue.longValue(), Double.MinValue.longValue())
+    val query_sql =
+      "select cast(d as decimal(1, 0)), cast(d as decimal(9, 1)), 
cast((f-55.12345) as decimal(9,1)), cast(f as decimal(4,2)), " +
+        "cast(f as decimal(32, 3)), cast(f as decimal(2, 1)), cast(d as 
decimal(38,3)) from test_tbl_8343"
+    spark.sql(create_table_sql);
+    spark.sql(insert_data_sql);
+    compareResultsAgainstVanillaSpark(query_sql, true, { _ => })
+    spark.sql("drop table test_tbl_8343")
+  }
+
 }
 // scalastyle:on line.size.limit
diff --git 
a/cpp-ch/local-engine/Functions/SparkFunctionCheckDecimalOverflow.cpp 
b/cpp-ch/local-engine/Functions/SparkFunctionCheckDecimalOverflow.cpp
index 8b5a7eff65..73fc7b5d4e 100644
--- a/cpp-ch/local-engine/Functions/SparkFunctionCheckDecimalOverflow.cpp
+++ b/cpp-ch/local-engine/Functions/SparkFunctionCheckDecimalOverflow.cpp
@@ -25,6 +25,7 @@
 #include <Functions/FunctionFactory.h>
 #include <Functions/FunctionHelpers.h>
 #include <Functions/IFunction.h>
+#include <typeinfo>
 
 namespace DB
 {
@@ -34,6 +35,7 @@ extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
 extern const int ILLEGAL_COLUMN;
 extern const int TYPE_MISMATCH;
+extern const int NOT_IMPLEMENTED;
 }
 }
 
@@ -78,7 +80,7 @@ public:
 
     DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) 
const override
     {
-        if (!isDecimal(arguments[0].type) || !isInteger(arguments[1].type) || 
!isInteger(arguments[2].type))
+        if ((!isDecimal(arguments[0].type) && 
!isNativeNumber(arguments[0].type)) || !isInteger(arguments[1].type) || 
!isInteger(arguments[2].type))
             throw Exception(
                 ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
                 "Illegal type {} {} {} of argument of function {}",
@@ -86,17 +88,14 @@ public:
                 arguments[1].type->getName(),
                 arguments[2].type->getName(),
                 getName());
-
         UInt32 precision = extractArgument(arguments[1]);
         UInt32 scale = extractArgument(arguments[2]);
-
         auto return_type = createDecimal<DataTypeDecimal>(precision, scale);
         if constexpr (exception_mode == CheckExceptionMode::Null)
         {
             if (!arguments[0].type->isNullable())
                 return std::make_shared<DataTypeNullable>(return_type);
         }
-
         return return_type;
     }
 
@@ -113,19 +112,15 @@ public:
             using Types = std::decay_t<decltype(types)>;
             using FromDataType = typename Types::LeftType;
             using ToDataType = typename Types::RightType;
-
-            if constexpr (IsDataTypeDecimal<FromDataType>)
+            if constexpr (IsDataTypeDecimal<FromDataType> || 
IsDataTypeNumber<FromDataType>)
             {
                 using FromFieldType = typename FromDataType::FieldType;
-                using ColVecType = ColumnDecimal<FromFieldType>;
-
-                if (const ColVecType * col_vec = 
checkAndGetColumn<ColVecType>(src_column.column.get()))
+                if (const ColumnVectorOrDecimal<FromFieldType> * col_vec = 
checkAndGetColumn<ColumnVectorOrDecimal<FromFieldType>>(src_column.column.get()))
                 {
-                    executeInternal<FromFieldType, ToDataType>(*col_vec, 
result_column, input_rows_count, precision, scale);
+                    executeInternal<FromDataType, ToDataType>(*col_vec, 
result_column, input_rows_count, precision, scale);
                     return true;
                 }
             }
-
             throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal 
column while execute function {}", getName());
         };
 
@@ -146,17 +141,28 @@ public:
     }
 
 private:
-    template <typename T, typename ToDataType>
+    template <typename FromDataType, typename ToDataType>
+    requires(IsDataTypeDecimal<ToDataType> && (IsDataTypeDecimal<FromDataType> 
|| IsDataTypeNumber<FromDataType>))
     static void executeInternal(
-        const ColumnDecimal<T> & col_source, ColumnPtr & result_column, size_t 
input_rows_count, UInt32 precision, UInt32 scale_to)
+        const ColumnVectorOrDecimal<typename FromDataType::FieldType> & 
col_source, ColumnPtr & result_column, size_t input_rows_count, UInt32 
precision, UInt32 scale_to)
     {
         using ToFieldType = typename ToDataType::FieldType;
         using ToColumnType = typename ToDataType::ColumnType;
+        using T = typename FromDataType::FieldType;
 
         ColumnUInt8::MutablePtr col_null_map_to;
         ColumnUInt8::Container * vec_null_map_to [[maybe_unused]] = nullptr;
-        auto scale_from = col_source.getScale();
-
+        UInt32 scale_from = 0;
+        using ToFieldNativeType = typename ToFieldType::NativeType;
+        ToFieldNativeType decimal_int_part_max = 0;
+        ToFieldNativeType decimal_int_part_min = 0;
+        if constexpr (IsDataTypeDecimal<FromDataType>)
+            scale_from = col_source.getScale();
+        else
+        {
+            decimal_int_part_max = 
DecimalUtils::scaleMultiplier<ToFieldNativeType>(precision - scale_to) - 1;
+            decimal_int_part_min = 1 - 
DecimalUtils::scaleMultiplier<ToFieldNativeType>(precision - scale_to);
+        }
         if constexpr (exception_mode == CheckExceptionMode::Null)
         {
             col_null_map_to = ColumnUInt8::create(input_rows_count, false);
@@ -170,17 +176,17 @@ private:
         auto & datas = col_source.getData();
         for (size_t i = 0; i < input_rows_count; ++i)
         {
-            // bool overflow = outOfDigits<T>(datas[i], precision, scale_from, 
scale_to);
             ToFieldType result;
-            bool success = convertToDecimalImpl<T, ToDataType>(datas[i], 
precision, scale_from, scale_to, result);
-
-            if (success)
+            bool success = convertToDecimalImpl<FromDataType, 
ToDataType>(datas[i], precision, scale_from, scale_to, decimal_int_part_max, 
decimal_int_part_min, result);
+            if constexpr (exception_mode == CheckExceptionMode::Null)
+            {
                 vec_to[i] = static_cast<ToFieldType>(result);
+                (*vec_null_map_to)[i] = !success;
+            }
             else
             {
-                vec_to[i] = static_cast<ToFieldType>(0);
-                if constexpr (exception_mode == CheckExceptionMode::Null)
-                    (*vec_null_map_to)[i] = static_cast<UInt8>(1);
+                if (success)
+                    vec_to[i] = static_cast<ToFieldType>(result);
                 else
                     throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal 
value is overflow.");
             }
@@ -192,20 +198,50 @@ private:
             result_column = std::move(col_to);
     }
 
-    template <is_decimal FromFieldType, typename ToDataType>
+    template <typename FromDataType, typename ToDataType>
     requires(IsDataTypeDecimal<ToDataType>)
     static bool convertToDecimalImpl(
-        const FromFieldType & decimal, UInt32 precision_to, UInt32 scale_from, 
UInt32 scale_to, typename ToDataType::FieldType & result)
+        const FromDataType::FieldType & value,
+        UInt32 precision_to,
+        UInt32 scale_from,
+        UInt32 scale_to,
+        typename ToDataType::FieldType::NativeType decimal_int_part_max,
+        typename ToDataType::FieldType::NativeType decimal_int_part_min,
+        typename ToDataType::FieldType & result)
     {
+        using FromFieldType = typename FromDataType::FieldType;
         if constexpr (std::is_same_v<FromFieldType, Decimal32>)
-            return convertDecimalsImpl<DataTypeDecimal<Decimal32>, 
ToDataType>(decimal, precision_to, scale_from, scale_to, result);
-
+            return convertDecimalsImpl<DataTypeDecimal<Decimal32>, 
ToDataType>(value, precision_to, scale_from, scale_to, result);
         else if constexpr (std::is_same_v<FromFieldType, Decimal64>)
-            return convertDecimalsImpl<DataTypeDecimal<Decimal64>, 
ToDataType>(decimal, precision_to, scale_from, scale_to, result);
+            return convertDecimalsImpl<DataTypeDecimal<Decimal64>, 
ToDataType>(value, precision_to, scale_from, scale_to, result);
         else if constexpr (std::is_same_v<FromFieldType, Decimal128>)
-            return convertDecimalsImpl<DataTypeDecimal<Decimal128>, 
ToDataType>(decimal, precision_to, scale_from, scale_to, result);
+            return convertDecimalsImpl<DataTypeDecimal<Decimal128>, 
ToDataType>(value, precision_to, scale_from, scale_to, result);
+        else if constexpr (std::is_same_v<FromFieldType, Decimal256>)
+            return convertDecimalsImpl<DataTypeDecimal<Decimal256>, 
ToDataType>(value, precision_to, scale_from, scale_to, result);
+        else if constexpr (IsDataTypeNumber<FromDataType> && 
!std::is_same_v<FromFieldType, BFloat16>)
+            return convertNumberToDecimalImpl<DataTypeNumber<FromFieldType>, 
ToDataType>(value, scale_to, decimal_int_part_max, decimal_int_part_min, 
result);
         else
-            return convertDecimalsImpl<DataTypeDecimal<Decimal256>, 
ToDataType>(decimal, precision_to, scale_from, scale_to, result);
+            throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Convert from {} type 
to decimal type is not implemented.", typeid(value).name());
+    }
+
+    template <typename FromDataType, typename ToDataType>
+    requires(IsDataTypeNumber<FromDataType> && IsDataTypeDecimal<ToDataType>)
+    static inline bool convertNumberToDecimalImpl(
+        const typename FromDataType::FieldType & value,
+        UInt32 scale,
+        typename ToDataType::FieldType::NativeType decimal_int_part_max,
+        typename ToDataType::FieldType::NativeType decimal_int_part_min,
+        typename ToDataType::FieldType & result)
+    {
+        using FromFieldType = typename FromDataType::FieldType;
+        using ToFieldNativeType = typename ToDataType::FieldType::NativeType;
+        ToFieldNativeType int_part = 0;
+        if constexpr (std::is_same_v<FromFieldType, Float32> || 
std::is_same_v<FromFieldType, Float64>)
+            int_part = static_cast<ToFieldNativeType>(value);
+        else
+            int_part = value;
+
+        return int_part >= decimal_int_part_min && int_part <= 
decimal_int_part_max && tryConvertToDecimal<FromDataType, ToDataType>(value, 
scale, result);
     }
 
     template <typename FromDataType, typename ToDataType>
diff --git a/cpp-ch/local-engine/Parser/ExpressionParser.cpp 
b/cpp-ch/local-engine/Parser/ExpressionParser.cpp
index 400d8c28df..a50590aaf7 100644
--- a/cpp-ch/local-engine/Parser/ExpressionParser.cpp
+++ b/cpp-ch/local-engine/Parser/ExpressionParser.cpp
@@ -313,7 +313,6 @@ ExpressionParser::NodeRawConstPtr 
ExpressionParser::parseExpression(ActionsDAG &
             DataTypePtr denull_input_type = removeNullable(input_type);
             DataTypePtr output_type = TypeParser::parseType(substrait_type);
             DataTypePtr denull_output_type = removeNullable(output_type);
-
             const ActionsDAG::Node * result_node = nullptr;
             if (substrait_type.has_binary())
             {
@@ -336,11 +335,15 @@ ExpressionParser::NodeRawConstPtr 
ExpressionParser::parseExpression(ActionsDAG &
                 String function_name = "sparkCastFloatTo" + 
denull_output_type->getName();
                 result_node = toFunctionNode(actions_dag, function_name, args);
             }
-            else if ((isDecimal(denull_input_type) && 
substrait_type.has_decimal()))
+            else if ((isDecimal(denull_input_type) || 
isNativeNumber(denull_input_type)) && substrait_type.has_decimal())
             {
-                args.emplace_back(addConstColumn(actions_dag, 
std::make_shared<DataTypeInt32>(), substrait_type.decimal().precision()));
-                args.emplace_back(addConstColumn(actions_dag, 
std::make_shared<DataTypeInt32>(), substrait_type.decimal().scale()));
-                result_node = toFunctionNode(actions_dag, 
"checkDecimalOverflowSparkOrNull", args);
+                int decimal_precision = substrait_type.decimal().precision();
+                if (decimal_precision)
+                {
+                    args.emplace_back(addConstColumn(actions_dag, 
std::make_shared<DataTypeInt32>(), decimal_precision));
+                    args.emplace_back(addConstColumn(actions_dag, 
std::make_shared<DataTypeInt32>(), substrait_type.decimal().scale()));
+                    result_node = toFunctionNode(actions_dag, 
"checkDecimalOverflowSparkOrNull", args);
+                }
             }
             else if (isMap(denull_input_type) && isString(denull_output_type))
             {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to