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

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new d5f3b8f26b2 branch-3.0: [fix](function) support new functions (#50470)
d5f3b8f26b2 is described below

commit d5f3b8f26b2447bbfc1951aa60f34f4c5e5a8aee
Author: Tiewei Fang <[email protected]>
AuthorDate: Tue May 6 09:56:51 2025 +0800

    branch-3.0: [fix](function) support new functions (#50470)
    
    picked form:
    1. #49392
    2. #49084
---
 be/src/vec/functions/function_json.cpp             |  35 +-
 be/src/vec/functions/function_string.cpp           |  58 +++-
 be/src/vec/functions/function_string.h             | 361 +++++++++++++++++++++
 be/src/vec/functions/function_totype.h             |  40 ++-
 be/test/vec/function/function_json_test.cpp        |  73 +++++
 be/test/vec/function/function_math_test.cpp        |  59 ++++
 be/test/vec/function/function_string_test.cpp      |  22 +-
 .../doris/catalog/BuiltinScalarFunctions.java      |   6 +
 .../expressions/functions/scalar/FormatRound.java  |  74 +++++
 .../functions/scalar/JsonExtractNoQuotes.java      |  69 ++++
 .../expressions/functions/scalar/UnhexNull.java    |  71 ++++
 .../expressions/visitor/ScalarFunctionVisitor.java |  15 +
 gensrc/script/doris_builtins_functions.py          |   6 +
 .../json_functions/test_json_function.out          | Bin 1295 -> 1428 bytes
 .../math_functions/test_format_round.out           | Bin 0 -> 681 bytes
 .../string_functions/test_string_function.out      | Bin 4892 -> 5056 bytes
 .../json_functions/test_json_function.groovy       |   7 +
 .../math_functions/test_format_round.groovy        |  64 ++++
 .../string_functions/test_string_function.groovy   |   8 +
 19 files changed, 945 insertions(+), 23 deletions(-)

diff --git a/be/src/vec/functions/function_json.cpp 
b/be/src/vec/functions/function_json.cpp
index 923e269904f..952a8b6ca57 100644
--- a/be/src/vec/functions/function_json.cpp
+++ b/be/src/vec/functions/function_json.cpp
@@ -841,8 +841,17 @@ struct FunctionJsonQuoteImpl {
     }
 };
 
-struct FunctionJsonExtractImpl {
+struct JsonExtractName {
     static constexpr auto name = "json_extract";
+};
+
+struct JsonExtractNoQuotesName {
+    static constexpr auto name = "json_extract_no_quotes";
+};
+
+template <typename Name, bool remove_quotes>
+struct FunctionJsonExtractImpl {
+    static constexpr auto name = Name::name;
 
     static rapidjson::Value parse_json(const ColumnString* json_col, const 
ColumnString* path_col,
                                        rapidjson::Document::AllocatorType& 
allocator, const int row,
@@ -904,11 +913,20 @@ struct FunctionJsonExtractImpl {
                 null_map[row] = 1;
                 result_column.insert_default();
             } else {
-                // write value as string
-                buf.Clear();
-                writer.Reset(buf);
-                value.Accept(writer);
-                result_column.insert_data(buf.GetString(), buf.GetSize());
+                // Check if the value is a string
+                if (value.IsString() && remove_quotes) {
+                    // Get the string value without quotes
+                    const char* str_ptr = value.GetString();
+                    size_t len = value.GetStringLength();
+                    // Insert without quotes
+                    result_column.insert_data(str_ptr, len);
+                } else {
+                    // Write value as string for other types
+                    buf.Clear();
+                    writer.Reset(buf);
+                    value.Accept(writer);
+                    result_column.insert_data(buf.GetString(), buf.GetSize());
+                }
             }
         };
         if (data_columns.size() == 2) {
@@ -1647,7 +1665,10 @@ void register_function_json(SimpleFunctionFactory& 
factory) {
     
factory.register_function<FunctionJsonAlwaysNotNullable<FunctionJsonArrayImpl>>();
     
factory.register_function<FunctionJsonAlwaysNotNullable<FunctionJsonObjectImpl>>();
     factory.register_function<FunctionJson<FunctionJsonQuoteImpl>>();
-    factory.register_function<FunctionJsonNullable<FunctionJsonExtractImpl>>();
+    factory.register_function<
+            FunctionJsonNullable<FunctionJsonExtractImpl<JsonExtractName, 
false>>>();
+    factory.register_function<
+            
FunctionJsonNullable<FunctionJsonExtractImpl<JsonExtractNoQuotesName, true>>>();
 
     factory.register_function<FunctionJsonValid>();
     factory.register_function<FunctionJsonContains>();
diff --git a/be/src/vec/functions/function_string.cpp 
b/be/src/vec/functions/function_string.cpp
index 1ba9eefebc4..064b3a7af22 100644
--- a/be/src/vec/functions/function_string.cpp
+++ b/be/src/vec/functions/function_string.cpp
@@ -879,9 +879,19 @@ public:
     }
 };
 
+struct UnHexImplEmpty {
+    static constexpr auto name = "unhex";
+};
+
+struct UnHexImplNull {
+    static constexpr auto name = "unhex_null";
+};
+
 static constexpr int MAX_STACK_CIPHER_LEN = 1024 * 64;
+
+template <typename Name>
 struct UnHexImpl {
-    static constexpr auto name = "unhex";
+    static constexpr auto name = Name::name;
     using ReturnType = DataTypeString;
     using ColumnType = ColumnString;
 
@@ -957,6 +967,42 @@ struct UnHexImpl {
             }
 
             int outlen = hex_decode(source, srclen, dst);
+            StringOP::push_value_string(std::string_view(dst, outlen), i, 
dst_data, dst_offsets);
+        }
+
+        return Status::OK();
+    }
+
+    static Status vector(const ColumnString::Chars& data, const 
ColumnString::Offsets& offsets,
+                         ColumnString::Chars& dst_data, ColumnString::Offsets& 
dst_offsets,
+                         ColumnUInt8::Container* null_map_data) {
+        auto rows_count = offsets.size();
+        dst_offsets.resize(rows_count);
+
+        for (int i = 0; i < rows_count; ++i) {
+            const auto* source = reinterpret_cast<const char*>(&data[offsets[i 
- 1]]);
+            ColumnString::Offset srclen = offsets[i] - offsets[i - 1];
+
+            if (srclen == 0) {
+                StringOP::push_null_string(i, dst_data, dst_offsets, 
*null_map_data);
+                continue;
+            }
+
+            char dst_array[MAX_STACK_CIPHER_LEN];
+            char* dst = dst_array;
+
+            int cipher_len = srclen / 2;
+            std::unique_ptr<char[]> dst_uptr;
+            if (cipher_len > MAX_STACK_CIPHER_LEN) {
+                dst_uptr.reset(new char[cipher_len]);
+                dst = dst_uptr.get();
+            }
+
+            int outlen = hex_decode(source, srclen, dst);
+            if (outlen == 0) {
+                StringOP::push_null_string(i, dst_data, dst_offsets, 
*null_map_data);
+                continue;
+            }
 
             StringOP::push_value_string(std::string_view(dst, outlen), i, 
dst_data, dst_offsets);
         }
@@ -1230,8 +1276,9 @@ using FunctionToUpper = 
FunctionStringToString<TransferImpl<NameToUpper>, NameTo
 
 using FunctionToInitcap = FunctionStringToString<InitcapImpl, NameToInitcap>;
 
-using FunctionUnHex = FunctionStringEncode<UnHexImpl>;
-using FunctionToBase64 = FunctionStringEncode<ToBase64Impl>;
+using FunctionUnHex = FunctionStringEncode<UnHexImpl<UnHexImplEmpty>, false>;
+using FunctionUnHexNullable = FunctionStringEncode<UnHexImpl<UnHexImplNull>, 
true>;
+using FunctionToBase64 = FunctionStringEncode<ToBase64Impl, false>;
 using FunctionFromBase64 = FunctionStringOperateToNullType<FromBase64Impl>;
 
 using FunctionStringAppendTrailingCharIfAbsent =
@@ -1256,6 +1303,7 @@ void register_function_string(SimpleFunctionFactory& 
factory) {
     factory.register_function<FunctionAutoPartitionName>();
     factory.register_function<FunctionReverseCommon>();
     factory.register_function<FunctionUnHex>();
+    factory.register_function<FunctionUnHexNullable>();
     factory.register_function<FunctionToLower>();
     factory.register_function<FunctionToUpper>();
     factory.register_function<FunctionToInitcap>();
@@ -1300,6 +1348,10 @@ void register_function_string(SimpleFunctionFactory& 
factory) {
     factory.register_function<FunctionMoneyFormat<MoneyFormatInt64Impl>>();
     factory.register_function<FunctionMoneyFormat<MoneyFormatInt128Impl>>();
     factory.register_function<FunctionMoneyFormat<MoneyFormatDecimalImpl>>();
+    
factory.register_function<FunctionStringFormatRound<FormatRoundDoubleImpl>>();
+    
factory.register_function<FunctionStringFormatRound<FormatRoundInt64Impl>>();
+    
factory.register_function<FunctionStringFormatRound<FormatRoundInt128Impl>>();
+    
factory.register_function<FunctionStringFormatRound<FormatRoundDecimalImpl>>();
     factory.register_function<FunctionStringDigestOneArg<SM3Sum>>();
     factory.register_function<FunctionStringDigestOneArg<MD5Sum>>();
     factory.register_function<FunctionStringDigestSHA1>();
diff --git a/be/src/vec/functions/function_string.h 
b/be/src/vec/functions/function_string.h
index 0b5b38eb49e..7202d356523 100644
--- a/be/src/vec/functions/function_string.h
+++ b/be/src/vec/functions/function_string.h
@@ -22,6 +22,8 @@
 #include <algorithm>
 #include <array>
 #include <boost/iterator/iterator_facade.hpp>
+#include <boost/locale.hpp>
+#include <boost/multiprecision/cpp_dec_float.hpp>
 #include <climits>
 #include <cmath>
 #include <codecvt>
@@ -1776,6 +1778,44 @@ public:
     }
 };
 
+template <typename Impl>
+class FunctionStringFormatRound : public IFunction {
+public:
+    static constexpr auto name = "format_round";
+    static FunctionPtr create() { return 
std::make_shared<FunctionStringFormatRound>(); }
+    String get_name() const override { return name; }
+
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        if (arguments.size() != 2) {
+            throw doris::Exception(ErrorCode::INVALID_ARGUMENT,
+                                   "Function {} requires exactly 2 argument", 
name);
+        }
+        return std::make_shared<DataTypeString>();
+    }
+    DataTypes get_variadic_argument_types_impl() const override {
+        return Impl::get_variadic_argument_types();
+    }
+    size_t get_number_of_arguments() const override { return 2; }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        auto res_column = ColumnString::create();
+        ColumnPtr argument_column = block.get_by_position(arguments[0]).column;
+        ColumnPtr argument_column_2;
+        bool is_const;
+        std::tie(argument_column_2, is_const) =
+                unpack_if_const(block.get_by_position(arguments[1]).column);
+
+        auto result_column = assert_cast<ColumnString*>(res_column.get());
+
+        RETURN_IF_ERROR(Impl::execute(context, result_column, argument_column, 
argument_column_2,
+                                      input_rows_count));
+
+        block.replace_by_position(result, std::move(res_column));
+        return Status::OK();
+    }
+};
+
 class FunctionSplitPart : public IFunction {
 public:
     static constexpr auto name = "split_part";
@@ -3034,6 +3074,146 @@ static StringRef do_money_format(FunctionContext* 
context, const string& value)
 };
 
 } // namespace MoneyFormat
+
+namespace FormatRound {
+
+constexpr size_t MAX_FORMAT_LEN_DEC32() {
+    // Decimal(9, 0)
+    // Double the size to avoid some unexpected bug.
+    return 2 * (1 + 9 + (9 / 3) + 3);
+}
+
+constexpr size_t MAX_FORMAT_LEN_DEC64() {
+    // Decimal(18, 0)
+    // Double the size to avoid some unexpected bug.
+    return 2 * (1 + 18 + (18 / 3) + 3);
+}
+
+constexpr size_t MAX_FORMAT_LEN_DEC128V2() {
+    // DecimalV2 has at most 27 digits
+    // Double the size to avoid some unexpected bug.
+    return 2 * (1 + 27 + (27 / 3) + 3);
+}
+
+constexpr size_t MAX_FORMAT_LEN_DEC128V3() {
+    // Decimal(38, 0)
+    // Double the size to avoid some unexpected bug.
+    return 2 * (1 + 39 + (39 / 3) + 3);
+}
+
+constexpr size_t MAX_FORMAT_LEN_INT64() {
+    // INT_MIN = -9223372036854775807
+    // Double the size to avoid some unexpected bug.
+    return 2 * (1 + 20 + (20 / 3) + 3);
+}
+
+constexpr size_t MAX_FORMAT_LEN_INT128() {
+    // INT128_MIN = -170141183460469231731687303715884105728
+    return 2 * (1 + 39 + (39 / 3) + 3);
+}
+
+template <typename T, size_t N>
+StringRef do_format_round(FunctionContext* context, UInt32 scale, T int_value, 
T frac_value,
+                          Int32 decimal_places) {
+    static_assert(std::is_integral<T>::value);
+    const bool is_negative = int_value < 0 || frac_value < 0;
+
+    // do round to frac_part based on decimal_places
+    if (scale > decimal_places && decimal_places > 0) {
+        DCHECK(scale <= 38);
+        // do rounding, so we need to reserve decimal_places + 1 digits
+        auto multiplier =
+                common::exp10_i128(std::abs(static_cast<int>(scale - 
(decimal_places + 1))));
+        // do divide first to avoid overflow
+        // after round frac_value will be positive by design
+        frac_value = std::abs(static_cast<int>(frac_value / multiplier)) + 5;
+        frac_value /= 10;
+    } else if (scale < decimal_places && decimal_places > 0) {
+        // since scale <= decimal_places, overflow is impossible
+        frac_value = frac_value * common::exp10_i32(decimal_places - scale);
+    }
+
+    // Calculate power of 10 for decimal_places
+    T decimal_power = common::exp10_i32(decimal_places);
+    if (frac_value == decimal_power) {
+        if (is_negative) {
+            int_value -= 1;
+        } else {
+            int_value += 1;
+        }
+        frac_value = 0;
+    }
+
+    bool append_sign_manually = false;
+    if (is_negative && int_value == 0) {
+        append_sign_manually = true;
+    }
+
+    char local[N];
+    char* p = SimpleItoaWithCommas(int_value, local, sizeof(local));
+    const Int32 integer_str_len = N - (p - local);
+    const Int32 frac_str_len = decimal_places;
+    const Int32 whole_decimal_str_len = (append_sign_manually ? 1 : 0) + 
integer_str_len +
+                                        (decimal_places > 0 ? 1 : 0) + 
frac_str_len;
+
+    StringRef result = context->create_temp_string_val(whole_decimal_str_len);
+    char* result_data = const_cast<char*>(result.data);
+
+    if (append_sign_manually) {
+        memset(result_data, '-', 1);
+    }
+
+    memcpy(result_data + (append_sign_manually ? 1 : 0), p, integer_str_len);
+    if (decimal_places > 0) {
+        *(result_data + whole_decimal_str_len - (frac_str_len + 1)) = '.';
+    }
+
+    // Convert fractional part to string with proper padding
+    T remaining_frac = std::abs(static_cast<int>(frac_value));
+    for (int i = 0; i <= decimal_places - 1; ++i) {
+        *(result_data + whole_decimal_str_len - 1 - i) = '0' + (remaining_frac 
% 10);
+        remaining_frac /= 10;
+    }
+    return result;
+}
+
+// Note string value must be valid decimal string which contains two digits 
after the decimal point
+static StringRef do_format_round(FunctionContext* context, const string& value,
+                                 Int32 decimal_places) {
+    bool is_positive = (value[0] != '-');
+    int32_t result_len =
+            value.size() +
+            (value.size() - (is_positive ? (decimal_places + 2) : 
(decimal_places + 3))) / 3;
+    StringRef result = context->create_temp_string_val(result_len);
+    char* result_data = const_cast<char*>(result.data);
+    if (!is_positive) {
+        *result_data = '-';
+    }
+    for (int i = value.size() - (decimal_places + 2), j = result_len - 
(decimal_places + 2); i >= 0;
+         i = i - 3) {
+        *(result_data + j) = *(value.data() + i);
+        if (i - 1 < 0) {
+            break;
+        }
+        *(result_data + j - 1) = *(value.data() + i - 1);
+        if (i - 2 < 0) {
+            break;
+        }
+        *(result_data + j - 2) = *(value.data() + i - 2);
+        if (j - 3 > 1 || (j - 3 == 1 && is_positive)) {
+            *(result_data + j - 3) = ',';
+            j -= 4;
+        } else {
+            j -= 3;
+        }
+    }
+    memcpy(result_data + result_len - (decimal_places + 1),
+           value.data() + value.size() - (decimal_places + 1), (decimal_places 
+ 1));
+    return result;
+};
+
+} // namespace FormatRound
+
 struct MoneyFormatDoubleImpl {
     static DataTypes get_variadic_argument_types() { return 
{std::make_shared<DataTypeFloat64>()}; }
 
@@ -3174,6 +3354,187 @@ struct MoneyFormatDecimalImpl {
     }
 };
 
+struct FormatRoundDoubleImpl {
+    static DataTypes get_variadic_argument_types() {
+        return {std::make_shared<DataTypeFloat64>(), 
std::make_shared<vectorized::DataTypeInt32>()};
+    }
+
+    static Status execute(FunctionContext* context, ColumnString* 
result_column,
+                          const ColumnPtr col_ptr, ColumnPtr 
decimal_places_col_ptr,
+                          size_t input_rows_count) {
+        const auto& arg_column_data_2 =
+                assert_cast<const 
ColumnInt32*>(decimal_places_col_ptr.get())->get_data();
+        const auto* data_column = assert_cast<const 
ColumnFloat64*>(col_ptr.get());
+        // when scale is above 38, we will go here
+        for (size_t i = 0; i < input_rows_count; i++) {
+            int32_t decimal_places = arg_column_data_2[i];
+            if (decimal_places < 0) {
+                return Status::InvalidArgument(
+                        "The second argument is {}, it can not be less than 
0.", decimal_places);
+            }
+            // round to `decimal_places` decimal places
+            double value = 
MathFunctions::my_double_round(data_column->get_element(i),
+                                                          decimal_places, 
false, false);
+            StringRef str = FormatRound::do_format_round(
+                    context, fmt::format("{:.{}f}", value, decimal_places), 
decimal_places);
+            result_column->insert_data(str.data, str.size);
+        }
+        return Status::OK();
+    }
+};
+
+struct FormatRoundInt64Impl {
+    static DataTypes get_variadic_argument_types() {
+        return {std::make_shared<DataTypeInt64>(), 
std::make_shared<vectorized::DataTypeInt32>()};
+    }
+
+    static Status execute(FunctionContext* context, ColumnString* 
result_column,
+                          const ColumnPtr col_ptr, ColumnPtr 
decimal_places_col_ptr,
+                          size_t input_rows_count) {
+        const auto* data_column = assert_cast<const 
ColumnVector<Int64>*>(col_ptr.get());
+        const auto& arg_column_data_2 =
+                assert_cast<const 
ColumnInt32*>(decimal_places_col_ptr.get())->get_data();
+        for (size_t i = 0; i < input_rows_count; i++) {
+            int32_t decimal_places = arg_column_data_2[i];
+            if (decimal_places < 0) {
+                return Status::InvalidArgument(
+                        "The second argument is {}, it can not be less than 
0.", decimal_places);
+            }
+            Int64 value = data_column->get_element(i);
+            StringRef str =
+                    FormatRound::do_format_round<Int64, 
FormatRound::MAX_FORMAT_LEN_INT64()>(
+                            context, 0, value, 0, decimal_places);
+            result_column->insert_data(str.data, str.size);
+        }
+        return Status::OK();
+    }
+};
+
+struct FormatRoundInt128Impl {
+    static DataTypes get_variadic_argument_types() {
+        return {std::make_shared<DataTypeInt128>(), 
std::make_shared<vectorized::DataTypeInt32>()};
+    }
+
+    static Status execute(FunctionContext* context, ColumnString* 
result_column,
+                          const ColumnPtr col_ptr, ColumnPtr 
decimal_places_col_ptr,
+                          size_t input_rows_count) {
+        const auto* data_column = assert_cast<const 
ColumnVector<Int128>*>(col_ptr.get());
+        const auto& arg_column_data_2 =
+                assert_cast<const 
ColumnInt32*>(decimal_places_col_ptr.get())->get_data();
+        // SELECT 
money_format(170141183460469231731687303715884105728/*INT128_MAX + 1*/) will
+        // get "170,141,183,460,469,231,731,687,303,715,884,105,727.00" in 
doris,
+        // see 
https://github.com/apache/doris/blob/788abf2d7c3c7c2d57487a9608e889e7662d5fb2/be/src/vec/data_types/data_type_number_base.cpp#L124
+        for (size_t i = 0; i < input_rows_count; i++) {
+            int32_t decimal_places = arg_column_data_2[i];
+            if (decimal_places < 0) {
+                return Status::InvalidArgument(
+                        "The second argument is {}, it can not be less than 
0.", decimal_places);
+            }
+            Int128 value = data_column->get_element(i);
+            StringRef str =
+                    FormatRound::do_format_round<Int128, 
FormatRound::MAX_FORMAT_LEN_INT128()>(
+                            context, 0, value, 0, decimal_places);
+            result_column->insert_data(str.data, str.size);
+        }
+        return Status::OK();
+    }
+};
+
+struct FormatRoundDecimalImpl {
+    static DataTypes get_variadic_argument_types() {
+        return {std::make_shared<DataTypeDecimal<Decimal128V2>>(27, 9),
+                std::make_shared<vectorized::DataTypeInt32>()};
+    }
+
+    static Status execute(FunctionContext* context, ColumnString* 
result_column, ColumnPtr col_ptr,
+                          ColumnPtr decimal_places_col_ptr, size_t 
input_rows_count) {
+        const auto& arg_column_data_2 =
+                assert_cast<const 
ColumnInt32*>(decimal_places_col_ptr.get())->get_data();
+        if (auto* decimalv2_column = 
check_and_get_column<ColumnDecimal<Decimal128V2>>(*col_ptr)) {
+            for (size_t i = 0; i < input_rows_count; i++) {
+                int32_t decimal_places = arg_column_data_2[i];
+                if (decimal_places < 0) {
+                    return Status::InvalidArgument(
+                            "The second argument is {}, it can not be less 
than 0.",
+                            decimal_places);
+                }
+                const Decimal128V2& dec128 = decimalv2_column->get_element(i);
+                DecimalV2Value value = DecimalV2Value(dec128.value);
+                // unified_frac_value has 3 digits
+                auto unified_frac_value = value.frac_value() / 1000000;
+                StringRef str =
+                        FormatRound::do_format_round<Int128,
+                                                     
FormatRound::MAX_FORMAT_LEN_DEC128V2()>(
+                                context, 3, value.int_value(), 
unified_frac_value, decimal_places);
+
+                result_column->insert_data(str.data, str.size);
+            }
+        } else if (auto* decimal32_column =
+                           
check_and_get_column<ColumnDecimal<Decimal32>>(*col_ptr)) {
+            const UInt32 scale = decimal32_column->get_scale();
+            for (size_t i = 0; i < input_rows_count; i++) {
+                int32_t decimal_places = arg_column_data_2[i];
+                if (decimal_places < 0) {
+                    return Status::InvalidArgument(
+                            "The second argument is {}, it can not be less 
than 0.",
+                            decimal_places);
+                }
+                const Decimal32& frac_part = 
decimal32_column->get_fractional_part(i);
+                const Decimal32& whole_part = 
decimal32_column->get_whole_part(i);
+                StringRef str =
+                        FormatRound::do_format_round<Int64, 
FormatRound::MAX_FORMAT_LEN_DEC32()>(
+                                context, scale, 
static_cast<Int64>(whole_part.value),
+                                static_cast<Int64>(frac_part.value), 
decimal_places);
+
+                result_column->insert_data(str.data, str.size);
+            }
+        } else if (auto* decimal64_column =
+                           
check_and_get_column<ColumnDecimal<Decimal64>>(*col_ptr)) {
+            const UInt32 scale = decimal64_column->get_scale();
+            for (size_t i = 0; i < input_rows_count; i++) {
+                int32_t decimal_places = arg_column_data_2[i];
+                if (decimal_places < 0) {
+                    return Status::InvalidArgument(
+                            "The second argument is {}, it can not be less 
than 0.",
+                            decimal_places);
+                }
+                const Decimal64& frac_part = 
decimal64_column->get_fractional_part(i);
+                const Decimal64& whole_part = 
decimal64_column->get_whole_part(i);
+
+                StringRef str =
+                        FormatRound::do_format_round<Int64, 
FormatRound::MAX_FORMAT_LEN_DEC64()>(
+                                context, scale, whole_part.value, 
frac_part.value, decimal_places);
+
+                result_column->insert_data(str.data, str.size);
+            }
+        } else if (auto* decimal128_column =
+                           
check_and_get_column<ColumnDecimal<Decimal128V3>>(*col_ptr)) {
+            const UInt32 scale = decimal128_column->get_scale();
+            for (size_t i = 0; i < input_rows_count; i++) {
+                int32_t decimal_places = arg_column_data_2[i];
+                if (decimal_places < 0) {
+                    return Status::InvalidArgument(
+                            "The second argument is {}, it can not be less 
than 0.",
+                            decimal_places);
+                }
+                const Decimal128V3& frac_part = 
decimal128_column->get_fractional_part(i);
+                const Decimal128V3& whole_part = 
decimal128_column->get_whole_part(i);
+
+                StringRef str =
+                        FormatRound::do_format_round<Int128,
+                                                     
FormatRound::MAX_FORMAT_LEN_DEC128V3()>(
+                                context, scale, whole_part.value, 
frac_part.value, decimal_places);
+
+                result_column->insert_data(str.data, str.size);
+            }
+        } else {
+            return Status::InternalError("Not supported input argument type 
{}",
+                                         col_ptr->get_name());
+        }
+        return Status::OK();
+    }
+};
+
 class FunctionStringLocatePos : public IFunction {
 public:
     static constexpr auto name = "locate";
diff --git a/be/src/vec/functions/function_totype.h 
b/be/src/vec/functions/function_totype.h
index e86f5444299..2647e0a84a9 100644
--- a/be/src/vec/functions/function_totype.h
+++ b/be/src/vec/functions/function_totype.h
@@ -489,7 +489,7 @@ public:
     }
 };
 
-template <typename Impl>
+template <typename Impl, bool is_allow_null>
 class FunctionStringEncode : public IFunction {
 public:
     static constexpr auto name = Impl::name;
@@ -501,6 +501,9 @@ public:
     size_t get_number_of_arguments() const override { return 1; }
 
     DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        if constexpr (is_allow_null) {
+            return make_nullable(std::make_shared<typename 
Impl::ReturnType>());
+        }
         return std::make_shared<typename Impl::ReturnType>();
     }
 
@@ -508,19 +511,34 @@ public:
                         size_t result, size_t input_rows_count) const override 
{
         auto& col_ptr = block.get_by_position(arguments[0]).column;
 
-        auto res = Impl::ColumnType::create();
-        if (const auto* col = 
check_and_get_column<ColumnString>(col_ptr.get())) {
-            auto col_res = Impl::ColumnType::create();
-            static_cast<void>(Impl::vector(col->get_chars(), 
col->get_offsets(),
-                                           col_res->get_chars(), 
col_res->get_offsets()));
-            block.replace_by_position(result, std::move(col_res));
+        if constexpr (is_allow_null) {
+            auto null_map = ColumnUInt8::create(input_rows_count, 0);
+            auto& null_map_data = null_map->get_data();
+            if (const auto* col = assert_cast<const 
ColumnString*>(col_ptr.get())) {
+                auto col_res = Impl::ColumnType::create();
+                static_cast<void>(Impl::vector(col->get_chars(), 
col->get_offsets(),
+                                               col_res->get_chars(), 
col_res->get_offsets(),
+                                               &null_map_data));
+                block.get_by_position(result).column =
+                        ColumnNullable::create(std::move(col_res), 
std::move(null_map));
+            } else {
+                return Status::RuntimeError("Illegal column {} of argument of 
function {}",
+                                            
block.get_by_position(arguments[0]).column->get_name(),
+                                            get_name());
+            }
         } else {
-            return Status::RuntimeError("Illegal column {} of argument of 
function {}",
-                                        
block.get_by_position(arguments[0]).column->get_name(),
-                                        get_name());
+            if (const auto* col = assert_cast<const 
ColumnString*>(col_ptr.get())) {
+                auto col_res = Impl::ColumnType::create();
+                static_cast<void>(Impl::vector(col->get_chars(), 
col->get_offsets(),
+                                               col_res->get_chars(), 
col_res->get_offsets()));
+                block.replace_by_position(result, std::move(col_res));
+            } else {
+                return Status::RuntimeError("Illegal column {} of argument of 
function {}",
+                                            
block.get_by_position(arguments[0]).column->get_name(),
+                                            get_name());
+            }
         }
         return Status::OK();
     }
 };
-
 } // namespace doris::vectorized
diff --git a/be/test/vec/function/function_json_test.cpp 
b/be/test/vec/function/function_json_test.cpp
index ceecadf64c1..988d4ca731a 100644
--- a/be/test/vec/function/function_json_test.cpp
+++ b/be/test/vec/function/function_json_test.cpp
@@ -85,4 +85,77 @@ TEST(FunctionJsonTEST, GetJsonStringTest) {
     static_cast<void>(check_function<DataTypeString, true>(func_name, 
input_types, data_set));
 }
 
+TEST(FunctionJsonTEST, JsonExtractTest) {
+    std::string json_extract_name = "json_extract";
+    std::string json_extract_no_quotes_name = "json_extract_no_quotes";
+    InputTypeSet input_types = {TypeIndex::String, TypeIndex::String};
+
+    // json_extract root
+    DataSet data_set = {
+            {{Null(), STRING("$")}, Null()},
+            {{STRING("null"), STRING("$")}, Null()},
+            {{STRING("true"), STRING("$")}, STRING("true")},
+            {{STRING("false"), STRING("$")}, STRING("false")},
+            {{STRING("100"), STRING("$")}, STRING("100")},                     
            //int8
+            {{STRING("10000"), STRING("$")}, STRING("10000")},                 
            // int16
+            {{STRING("1000000000"), STRING("$")}, STRING("1000000000")},       
            // int32
+            {{STRING("1152921504606846976"), STRING("$")}, 
STRING("1152921504606846976")}, // int64
+            {{STRING("6.18"), STRING("$")}, STRING("6.18")},                   
            // double
+            {{STRING("{}"), STRING("$")}, STRING("{}")}, // empty object
+
+            {{STRING(R"([1, 2, 3])"), STRING(R"($.[1])")}, STRING(R"(2)")},
+            {{STRING(R"({"id": 123, "name": "doris"})"), STRING(R"($.name)")},
+             STRING(R"("doris")")},
+            {{STRING(R"({"id": 123, "name": "doris"})"), STRING(R"($.id)")}, 
STRING(R"(123)")},
+            {{Null(), STRING(R"($.id)")}, Null()},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k1)")},
+             STRING(R"("v1")")},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k2.k21)")},
+             STRING(R"(6.6)")},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k2.k22)")},
+             STRING(R"([1,2,3])")},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k2.k22[1])")},
+             STRING(R"(2)")},
+    };
+
+    static_cast<void>(
+            check_function<DataTypeString, true>(json_extract_name, 
input_types, data_set));
+
+    data_set = {
+            {{Null(), STRING("$")}, Null()},
+            {{STRING("null"), STRING("$")}, Null()},
+            {{STRING("true"), STRING("$")}, STRING("true")},
+            {{STRING("false"), STRING("$")}, STRING("false")},
+            {{STRING("100"), STRING("$")}, STRING("100")},                     
            //int8
+            {{STRING("10000"), STRING("$")}, STRING("10000")},                 
            // int16
+            {{STRING("1000000000"), STRING("$")}, STRING("1000000000")},       
            // int32
+            {{STRING("1152921504606846976"), STRING("$")}, 
STRING("1152921504606846976")}, // int64
+            {{STRING("6.18"), STRING("$")}, STRING("6.18")},                   
            // double
+            {{STRING("{}"), STRING("$")}, STRING("{}")}, // empty object
+
+            {{STRING(R"([1, 2, 3])"), STRING(R"($.[1])")}, STRING(R"(2)")},
+            {{STRING(R"({"id": 123, "name": "doris"})"), STRING(R"($.name)")}, 
STRING(R"(doris)")},
+            {{STRING(R"({"id": 123, "name": "doris"})"), STRING(R"($.id)")}, 
STRING(R"(123)")},
+            {{Null(), STRING(R"($.id)")}, Null()},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k1)")},
+             STRING(R"(v1)")},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k2.k21)")},
+             STRING(R"(6.6)")},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k2.k22)")},
+             STRING(R"([1,2,3])")},
+            {{STRING(R"({"k1": "v1", "k2": { "k21": 6.6, "k22": [1, 2, 3] } 
})"),
+              STRING(R"($.k2.k22[1])")},
+             STRING(R"(2)")},
+    };
+    static_cast<void>(check_function<DataTypeString, 
true>(json_extract_no_quotes_name, input_types,
+                                                           data_set));
+}
+
 } // namespace doris::vectorized
diff --git a/be/test/vec/function/function_math_test.cpp 
b/be/test/vec/function/function_math_test.cpp
index bb24d141e4c..63a1b964a0c 100644
--- a/be/test/vec/function/function_math_test.cpp
+++ b/be/test/vec/function/function_math_test.cpp
@@ -525,4 +525,63 @@ TEST(MathFunctionTest, money_format_test) {
     }
 }
 
+TEST(MathFunctionTest, format_round_test) {
+    std::string func_name = "format_round";
+
+    {
+        InputTypeSet input_types = {TypeIndex::Int64, TypeIndex::Int32};
+        DataSet data_set = {{{Null(), INT(2)}, Null()},
+                            {{BIGINT(17014116), INT(2)}, 
VARCHAR("17,014,116.00")},
+                            {{BIGINT(-17014116), INT(2)}, 
VARCHAR("-17,014,116.00")},
+                            {{BIGINT(1), INT(0)}, VARCHAR("1")},
+                            {{BIGINT(123456), INT(0)}, VARCHAR("123,456")},
+                            {{BIGINT(123456), INT(3)}, VARCHAR("123,456.000")},
+                            {{BIGINT(123456), INT(10)}, 
VARCHAR("123,456.0000000000")},
+                            {{BIGINT(123456), INT(20)}, 
VARCHAR("123,456.00000000000000000000")}};
+
+        static_cast<void>(check_function<DataTypeString, true>(func_name, 
input_types, data_set));
+    }
+    {
+        InputTypeSet input_types = {TypeIndex::Int128, TypeIndex::Int32};
+        DataSet data_set = {
+                {{Null(), INT(2)}, Null()},
+                {{LARGEINT(17014116), INT(2)}, VARCHAR("17,014,116.00")},
+                {{LARGEINT(-17014116), INT(2)}, VARCHAR("-17,014,116.00")},
+                {{LARGEINT(1), INT(0)}, VARCHAR("1")},
+                {{LARGEINT(123456), INT(0)}, VARCHAR("123,456")},
+                {{LARGEINT(123456), INT(3)}, VARCHAR("123,456.000")},
+                {{LARGEINT(123456), INT(10)}, VARCHAR("123,456.0000000000")},
+                {{LARGEINT(123456), INT(20)}, 
VARCHAR("123,456.00000000000000000000")},
+                {{LARGEINT(123456789123456789), INT(2)}, 
VARCHAR("123,456,789,123,456,789.00")}};
+
+        static_cast<void>(check_function<DataTypeString, true>(func_name, 
input_types, data_set));
+    }
+    {
+        InputTypeSet input_types = {TypeIndex::Float64, TypeIndex::Int32};
+        DataSet data_set = {{{Null(), INT(2)}, Null()},
+                            {{DOUBLE(17014116.67), INT(2)}, 
VARCHAR("17,014,116.67")},
+                            {{DOUBLE(-17014116.67), INT(2)}, 
VARCHAR("-17,014,116.67")},
+                            {{DOUBLE(-123.45), INT(2)}, VARCHAR("-123.45")}};
+
+        static_cast<void>(check_function<DataTypeString, true>(func_name, 
input_types, data_set));
+    }
+    {
+        InputTypeSet input_types = {TypeIndex::Decimal128V2, TypeIndex::Int32};
+        DataSet data_set = {{{Null(), INT(2)}, Null()},
+                            {{DECIMALV2(17014116.67), INT(2)}, 
VARCHAR("17,014,116.67")},
+                            {{DECIMALV2(-17014116.67), INT(2)}, 
VARCHAR("-17,014,116.67")}};
+
+        static_cast<void>(check_function<DataTypeString, true>(func_name, 
input_types, data_set));
+    }
+    {
+        BaseInputTypeSet input_types = {TypeIndex::Decimal64, 
TypeIndex::Int32};
+        DataSet data_set = {
+                {{Null(), INT(2)}, Null()},
+                {{DECIMAL64(17014116, 670000000), INT(2)}, 
VARCHAR("17,014,116.67")},
+                {{DECIMAL64(-17014116, -670000000), INT(2)}, 
VARCHAR("-17,014,116.67")}};
+
+        check_function_all_arg_comb<DataTypeString, true>(func_name, 
input_types, data_set);
+    }
+}
+
 } // namespace doris::vectorized
diff --git a/be/test/vec/function/function_string_test.cpp 
b/be/test/vec/function/function_string_test.cpp
index 28ba69be200..9f1ffe438a1 100644
--- a/be/test/vec/function/function_string_test.cpp
+++ b/be/test/vec/function/function_string_test.cpp
@@ -2338,7 +2338,7 @@ TEST(function_string_test, function_hex_test) {
 }
 
 TEST(function_string_test, function_unhex_test) {
-    std::string func_name = "unhex";
+    std::string unhex_func_name = "unhex";
     BaseInputTypeSet input_types = {TypeIndex::String};
     DataSet data_set = {
             {{std::string("41624364456667")}, std::string("AbCdEfg")},
@@ -2354,7 +2354,25 @@ TEST(function_string_test, function_unhex_test) {
             {{std::string("20202020202B20202020202020323320")}, std::string("  
   +       23 ")},
             // {{std::string("!")}, Null()},
     };
-    check_function_all_arg_comb<DataTypeString, true>(func_name, input_types, 
data_set);
+    check_function_all_arg_comb<DataTypeString, true>(unhex_func_name, 
input_types, data_set);
+
+    std::string unhex_null_func_name = "unhex_null";
+    data_set = {
+            {{std::string("41624364456667")}, std::string("AbCdEfg")},
+            {{std::string("E4BDA0E5A5BD48454C4C4F")}, std::string("你好HELLO")},
+            {{std::string("")}, Null()},
+            {{Null()}, Null()},
+            {{std::string("21402324402A2028212623")}, std::string("!@#$@* 
(!&#")},
+            {{std::string("4A534B41422851405F5F21")}, 
std::string("JSKAB(Q@__!")},
+            {{std::string("M4D59207465737420537472E4BDA0E5A5BD2020")}, Null()},
+            {{std::string("2020202020202020202020202020202020")}, 
std::string("                 ")},
+            {{std::string("3233203132202D2D215F5F215F215F5F21")}, 
std::string("23 12 --!__!_!__!")},
+            {{std::string("3131322B202B202B")}, std::string("112+ + +")},
+            {{std::string("20202020202B20202020202020323320")}, std::string("  
   +       23 ")},
+            {{std::string("41G42")}, Null()},
+            {{std::string("!")}, Null()},
+    };
+    check_function_all_arg_comb<DataTypeString, true>(unhex_null_func_name, 
input_types, data_set);
 }
 
 TEST(function_string_test, function_coalesce_test) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
index de26a4f4d83..8452c5ad358 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java
@@ -180,6 +180,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.FindInSet;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.FirstSignificantSubdomain;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Floor;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Fmod;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.FormatRound;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Fpow;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.FromBase64;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.FromDays;
@@ -234,6 +235,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.IsIpv6String;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonArray;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.JsonContains;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtract;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtractNoQuotes;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonInsert;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonKeys;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonLength;
@@ -445,6 +447,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Trim;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.TrimIn;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Truncate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Unhex;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.UnhexNull;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.UnixTimestamp;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Upper;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.UrlDecode;
@@ -701,6 +704,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(JsonQuote.class, "json_quote"),
             scalar(JsonUnQuote.class, "json_unquote"),
             scalar(JsonExtract.class, "json_extract"),
+            scalar(JsonExtractNoQuotes.class, "json_extract_no_quotes"),
             scalar(JsonInsert.class, "json_insert"),
             scalar(JsonReplace.class, "json_replace"),
             scalar(JsonSet.class, "json_set"),
@@ -841,6 +845,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(Right.class, "right", "strright"),
             scalar(Round.class, "round"),
             scalar(RoundBankers.class, "round_bankers"),
+            scalar(FormatRound.class, "format_round"),
             scalar(Rpad.class, "rpad"),
             scalar(Rtrim.class, "rtrim"),
             scalar(RtrimIn.class, "rtrim_in"),
@@ -933,6 +938,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(TrimIn.class, "trim_in"),
             scalar(Truncate.class, "truncate"),
             scalar(Unhex.class, "unhex"),
+            scalar(UnhexNull.class, "unhex_null"),
             scalar(UnixTimestamp.class, "unix_timestamp"),
             scalar(Upper.class, "ucase", "upper"),
             scalar(Quote.class, "quote"),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FormatRound.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FormatRound.java
new file mode 100644
index 00000000000..368bcbab296
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FormatRound.java
@@ -0,0 +1,74 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.BigIntType;
+import org.apache.doris.nereids.types.DecimalV2Type;
+import org.apache.doris.nereids.types.DecimalV3Type;
+import org.apache.doris.nereids.types.DoubleType;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.LargeIntType;
+import org.apache.doris.nereids.types.StringType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/** FormatRound function */
+public class FormatRound extends ScalarFunction
+        implements BinaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(StringType.INSTANCE).args(BigIntType.INSTANCE, 
IntegerType.INSTANCE),
+            
FunctionSignature.ret(StringType.INSTANCE).args(LargeIntType.INSTANCE, 
IntegerType.INSTANCE),
+            
FunctionSignature.ret(StringType.INSTANCE).args(DoubleType.INSTANCE, 
IntegerType.INSTANCE),
+            
FunctionSignature.ret(StringType.INSTANCE).args(DecimalV2Type.SYSTEM_DEFAULT, 
IntegerType.INSTANCE),
+            
FunctionSignature.ret(StringType.INSTANCE).args(DecimalV3Type.WILDCARD, 
IntegerType.INSTANCE));
+
+    /**
+     * constructor with 2 or more arguments.
+     */
+    public FormatRound(Expression arg0, Expression arg1) {
+        super("format_round", arg0, arg1);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public FormatRound withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new FormatRound(children.get(0), children.get(1));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitFormatRound(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonExtractNoQuotes.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonExtractNoQuotes.java
new file mode 100644
index 00000000000..4f6b0b4a5e5
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/JsonExtractNoQuotes.java
@@ -0,0 +1,69 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.VarcharType;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'json_object'.
+ */
+public class JsonExtractNoQuotes extends ScalarFunction
+        implements ExplicitlyCastableSignature, AlwaysNullable {
+
+    public static final List<FunctionSignature> SIGNATURES =
+            ImmutableList.of(FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT)
+                    .varArgs(VarcharType.SYSTEM_DEFAULT, 
VarcharType.SYSTEM_DEFAULT));
+
+    /**
+     * constructor with 1 or more arguments.
+     */
+    public JsonExtractNoQuotes(Expression arg0, Expression arg1, Expression... 
varArgs) {
+        super("json_extract_no_quotes", ExpressionUtils.mergeArguments(arg0, 
arg1, varArgs));
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public JsonExtractNoQuotes withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() >= 2);
+        return new JsonExtractNoQuotes(children.get(0), children.get(1),
+                children.subList(2, children.size()).toArray(new 
Expression[0]));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitJsonExtractNoQuotes(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnhexNull.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnhexNull.java
new file mode 100644
index 00000000000..ba2ae5b59f1
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnhexNull.java
@@ -0,0 +1,71 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.VarcharType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'unhex_null'
+ */
+public class UnhexNull extends ScalarFunction
+        implements UnaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable, PropagateNullLiteral {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT).args(VarcharType.SYSTEM_DEFAULT),
+            
FunctionSignature.ret(StringType.INSTANCE).args(StringType.INSTANCE)
+    );
+
+    /**
+     * constructor with 1 argument.
+     */
+    public UnhexNull(Expression arg) {
+        super("unhex_null", arg);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public UnhexNull withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new UnhexNull(children.get(0));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitUnhexNull(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
index a4da8c4d4c5..2890787ddb7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java
@@ -188,6 +188,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.FindInSet;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.FirstSignificantSubdomain;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Floor;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Fmod;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.FormatRound;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Fpow;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.FromBase64;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.FromDays;
@@ -237,6 +238,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.IsIpv6String;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonArray;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.JsonContains;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtract;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.JsonExtractNoQuotes;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonInsert;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonKeys;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.JsonLength;
@@ -442,6 +444,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Trim;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.TrimIn;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Truncate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Unhex;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.UnhexNull;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.UnixTimestamp;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Upper;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.UrlDecode;
@@ -1373,6 +1376,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(jsonExtract, context);
     }
 
+    default R visitJsonExtractNoQuotes(JsonExtractNoQuotes jsonExtract, C 
context) {
+        return visitScalarFunction(jsonExtract, context);
+    }
+
     default R visitJsonKeys(JsonKeys jsonKeys, C context) {
         return visitScalarFunction(jsonKeys, context);
     }
@@ -1809,6 +1816,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(roundBankers, context);
     }
 
+    default R visitFormatRound(FormatRound formatRound, C context) {
+        return visitScalarFunction(formatRound, context);
+    }
+
     default R visitRpad(Rpad rpad, C context) {
         return visitScalarFunction(rpad, context);
     }
@@ -2137,6 +2148,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(unhex, context);
     }
 
+    default R visitUnhexNull(UnhexNull unhexNull, C context) {
+        return visitScalarFunction(unhexNull, context);
+    }
+
     default R visitUnixTimestamp(UnixTimestamp unixTimestamp, C context) {
         return visitScalarFunction(unixTimestamp, context);
     }
diff --git a/gensrc/script/doris_builtins_functions.py 
b/gensrc/script/doris_builtins_functions.py
index 933cd03e8af..99c17f7dfc8 100644
--- a/gensrc/script/doris_builtins_functions.py
+++ b/gensrc/script/doris_builtins_functions.py
@@ -1685,6 +1685,12 @@ visible_functions = {
         [['repeat'], 'STRING', ['STRING', 'INT'], 'ALWAYS_NULLABLE'],
         [['lpad'], 'STRING', ['STRING', 'INT', 'STRING'], 'ALWAYS_NULLABLE'],
         [['rpad'], 'STRING', ['STRING', 'INT', 'STRING'], 'ALWAYS_NULLABLE'],
+        [['format_round'], 'STRING', ['BIGINT'], ''],
+        [['format_round'], 'STRING', ['LARGEINT'], ''],
+        [['format_round'], 'STRING', ['DOUBLE', 'INT'], ''],
+        [['format_round'], 'STRING', ['DECIMAL32', 'INT'], ''],
+        [['format_round'], 'STRING', ['DECIMAL64', 'INT'], ''],
+        [['format_round'], 'STRING', ['DECIMAL128', 'INT'], ''],
         [['append_trailing_char_if_absent'], 'STRING', ['STRING', 'STRING'], 
'ALWAYS_NULLABLE'],
         [['length'], 'INT', ['STRING'], ''],
         [['crc32'], 'BIGINT', ['STRING'], ''],
diff --git 
a/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out
 
b/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out
index 45d1d1b9e03..1ec59c120dc 100644
Binary files 
a/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out
 and 
b/regression-test/data/query_p0/sql_functions/json_functions/test_json_function.out
 differ
diff --git 
a/regression-test/data/query_p0/sql_functions/math_functions/test_format_round.out
 
b/regression-test/data/query_p0/sql_functions/math_functions/test_format_round.out
new file mode 100644
index 00000000000..42ce5b1ff28
Binary files /dev/null and 
b/regression-test/data/query_p0/sql_functions/math_functions/test_format_round.out
 differ
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out
index 4af2997eda2..433447d8fff 100644
Binary files 
a/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out
 and 
b/regression-test/data/query_p0/sql_functions/string_functions/test_string_function.out
 differ
diff --git 
a/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy
 
b/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy
index 20ef0843e39..74545d6046d 100644
--- 
a/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/json_functions/test_json_function.groovy
@@ -79,4 +79,11 @@ suite("test_json_function", "arrow_flight_sql") {
     qt_sql """SELECT JSON_CONTAINS("",'1','\$.a')"""
 
     qt_sql """select k6, json_extract_string(cast(k7 as json), "\$.a") as x10 
from test_query_db.baseall group by k6, x10 order by 1,2; """
+    
+    qt_sql "SELECT json_extract_no_quotes('[1, 2, 3]', '\$.[1]');"
+    qt_sql "SELECT json_extract_no_quotes('{\"id\": 123, \"name\": 
\"doris\"}', '\$.name');"
+    qt_sql "SELECT json_extract_no_quotes('{\"id\": 123, \"name\": 
\"doris\"}', '\$.id', null);"
+    qt_sql "SELECT json_extract_no_quotes(null, '\$.id');"
+    qt_sql "SELECT json_extract_no_quotes('{\"k1\": \"v1\", \"k2\": { \"k21\": 
6.6, \"k22\": [1, 2, 3] } }', '\$.k1', '\$.k2');"
+    qt_sql "SELECT json_extract_no_quotes('{\"k1\": \"v1\", \"k2\": { \"k21\": 
6.6, \"k22\": [1, 2, 3] } }', '\$.k2.k21', '\$.k2.k22', '\$.k2.k22[1]');"
 }
diff --git 
a/regression-test/suites/query_p0/sql_functions/math_functions/test_format_round.groovy
 
b/regression-test/suites/query_p0/sql_functions/math_functions/test_format_round.groovy
new file mode 100644
index 00000000000..dfd4a057a36
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/math_functions/test_format_round.groovy
@@ -0,0 +1,64 @@
+// 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.
+
+suite("test_format_round", "p0") {
+
+    order_qt_format_round_0 """
+        select format_round(1234567.8910,5),
+                format_round(-1111.235,2),
+                format_round(123.49 ,0),
+                format_round(cast(1.44999 as double),5),
+                format_round(cast(1.44999 as decimal(20,2)),5);
+    """
+    order_qt_format_round_1 """ select format_round(1, 0) """
+    order_qt_format_round_2 """ select format_round(123456, 0) """
+    order_qt_format_round_3 """ select format_round(123456, 3) """
+    order_qt_format_round_4 """ select format_round(123456, 10) """
+    order_qt_format_round_5 """ select format_round(123456.123456, 0) """
+    order_qt_format_round_6 """ select format_round(123456.123456, 3) """
+    order_qt_format_round_7 """ select format_round(123456.123456, 8) """
+
+     sql """ DROP TABLE IF EXISTS test_format_round """
+        sql """
+        CREATE TABLE IF NOT EXISTS test_format_round (
+            `user_id` LARGEINT NOT NULL COMMENT "用户id",
+            `int_col` int COMMENT "",
+            `bigint_col` bigint COMMENT "",
+            `largeint_col` largeint COMMENT "",
+            `double_col` double COMMENT "",
+            `decimal_col` decimal COMMENT ""
+            )
+            DISTRIBUTED BY HASH(user_id) PROPERTIES("replication_num" = "1");
+        """
+
+        sql """ INSERT INTO test_format_round VALUES
+                    (1, 123, 123456, 123455677788, 123456.1234567, 
123456.1234567);
+            """
+        qt_select_default """ SELECT * FROM test_format_round t ORDER BY 
user_id; """
+
+    order_qt_format_round_8 """ select format_round(int_col, 6) from 
test_format_round"""
+    order_qt_format_round_9 """ select format_round(bigint_col, 6) from 
test_format_round"""
+    order_qt_format_round_10 """ select format_round(largeint_col, 6) from 
test_format_round"""
+    order_qt_format_round_12 """ select format_round(double_col, 6) from 
test_format_round"""
+    order_qt_format_round_13 """ select format_round(decimal_col, 6) from 
test_format_round"""
+
+    test {
+        sql """select format_round(1234567.8910, -1) """
+        exception "it can not be less than 0"
+    }
+
+}
\ No newline at end of file
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
index c1a3e6b57fe..805bfd8e46d 100644
--- 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
@@ -71,6 +71,14 @@ suite("test_string_function", "arrow_flight_sql") {
     qt_sql "select unhex('');"
     qt_sql "select unhex(NULL);"
 
+
+    qt_sql_unhex_null "select unhex_null('@');"
+    qt_sql_unhex_null "select unhex_null('68656C6C6F2C646F726973');"
+    qt_sql_unhex_null "select unhex_null('41');"
+    qt_sql_unhex_null "select unhex_null('4142');"
+    qt_sql_unhex_null "select unhex_null('');"
+    qt_sql_unhex_null "select unhex_null(NULL);"
+
     qt_sql_instr "select instr(\"abc\", \"b\");"
     qt_sql_instr "select instr(\"abc\", \"d\");"
     qt_sql_instr "select instr(\"abc\", null);"


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

Reply via email to