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 b085028a896 branch-3.0: [feature](functions) Pick funcs to 3.0 (#50481)
b085028a896 is described below

commit b085028a89648cc25b7782b36055dbf37960e2b4
Author: Socrates <[email protected]>
AuthorDate: Tue May 6 09:59:04 2025 +0800

    branch-3.0: [feature](functions) Pick funcs to 3.0 (#50481)
    
    pick prs:
    #49119
    #49122
    #49142
    #49262
---
 be/cmake/thirdparty.cmake                          |   5 +-
 .../function_date_or_datetime_computation.cpp      |   3 +
 .../function_date_or_datetime_computation.h        | 208 ++++++++++++++++++
 be/src/vec/functions/function_map.cpp              | 156 ++++++++++++++
 be/src/vec/functions/function_string.cpp           |   1 +
 be/src/vec/functions/function_string.h             | 136 ++++++++++++
 be/test/vec/function/function_string_test.cpp      |  51 +++++
 be/test/vec/function/function_time_test.cpp        | 107 ++++++++++
 .../doris/catalog/BuiltinScalarFunctions.java      |   8 +
 .../executable/DateTimeExtractAndTransform.java    |  78 +++++++
 .../functions/scalar/MonthsBetween.java            |  83 ++++++++
 .../expressions/functions/scalar/NextDay.java      |  74 +++++++
 .../expressions/functions/scalar/StrToMap.java     | 110 ++++++++++
 .../expressions/functions/scalar/XpathString.java  |  72 +++++++
 .../expressions/visitor/ScalarFunctionVisitor.java |  20 ++
 .../nereids/rules/expression/FoldConstantTest.java |  57 +++++
 .../string_functions/test_months_between.out       | Bin 0 -> 4763 bytes
 .../string_functions/test_next_day.out             | Bin 0 -> 2863 bytes
 .../string_functions/test_str_to_map.out           | Bin 0 -> 17405 bytes
 .../string_functions/test_xpath_string.out         | Bin 0 -> 1850 bytes
 .../fold_constant_date_arithmatic.groovy           |  29 +++
 .../string_functions/test_months_between.groovy    | 147 +++++++++++++
 .../string_functions/test_next_day.groovy          | 129 ++++++++++++
 .../string_functions/test_str_to_map.groovy        | 232 +++++++++++++++++++++
 .../string_functions/test_xpath_string.groovy      | 149 +++++++++++++
 25 files changed, 1854 insertions(+), 1 deletion(-)

diff --git a/be/cmake/thirdparty.cmake b/be/cmake/thirdparty.cmake
index 67db4aa2733..764719ae00c 100644
--- a/be/cmake/thirdparty.cmake
+++ b/be/cmake/thirdparty.cmake
@@ -174,4 +174,7 @@ endif()
 
 add_thirdparty(icuuc LIB64)
 add_thirdparty(icui18n LIB64)
-add_thirdparty(icudata LIB64)
\ No newline at end of file
+add_thirdparty(icudata LIB64)
+
+
+add_thirdparty(pugixml LIB64)
diff --git a/be/src/vec/functions/function_date_or_datetime_computation.cpp 
b/be/src/vec/functions/function_date_or_datetime_computation.cpp
index f6bf806ad46..50e6db475f5 100644
--- a/be/src/vec/functions/function_date_or_datetime_computation.cpp
+++ b/be/src/vec/functions/function_date_or_datetime_computation.cpp
@@ -122,6 +122,8 @@ void 
register_function_date_time_computation(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionMinutesDiff>();
     factory.register_function<FunctionSecondsDiff>();
 
+    factory.register_function<FunctionNextDay>();
+
     factory.register_function<FunctionToYearWeekTwoArgs>();
     factory.register_function<FunctionToWeekTwoArgs>();
 
@@ -135,6 +137,7 @@ void 
register_function_date_time_computation(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionMicroSecToDateTime>();
     factory.register_function<FunctionMilliSecToDateTime>();
     factory.register_function<FunctionSecToDateTime>();
+    factory.register_function<FunctionMonthsBetween>();
 
     // alias
     factory.register_alias("days_add", "date_add");
diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h 
b/be/src/vec/functions/function_date_or_datetime_computation.h
index ac18965749e..ab887812249 100644
--- a/be/src/vec/functions/function_date_or_datetime_computation.h
+++ b/be/src/vec/functions/function_date_or_datetime_computation.h
@@ -1183,4 +1183,212 @@ protected:
     }
 };
 
+class FunctionMonthsBetween : public IFunction {
+public:
+    static constexpr auto name = "months_between";
+    static FunctionPtr create() { return 
std::make_shared<FunctionMonthsBetween>(); }
+    String get_name() const override { return name; }
+    size_t get_number_of_arguments() const override { return 3; }
+
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        return std::make_shared<DataTypeFloat64>();
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        CHECK_EQ(arguments.size(), 3);
+        auto res = ColumnFloat64::create();
+
+        bool date_consts[2];
+        date_consts[0] = 
is_column_const(*block.get_by_position(arguments[0]).column);
+        date_consts[1] = 
is_column_const(*block.get_by_position(arguments[1]).column);
+        ColumnPtr date_cols[2];
+        // convert const columns to full columns if necessary
+        default_preprocess_parameter_columns(date_cols, date_consts, {0, 1}, 
block, arguments);
+
+        const auto& [col3, col3_const] =
+                unpack_if_const(block.get_by_position(arguments[2]).column);
+
+        const auto& date1_col = *assert_cast<const 
ColumnDateV2*>(date_cols[0].get());
+        const auto& date2_col = *assert_cast<const 
ColumnDateV2*>(date_cols[1].get());
+        const auto& round_off_col = *assert_cast<const 
ColumnBool*>(col3.get());
+
+        if (date_consts[0] && date_consts[1]) {
+            execute_vector<true, false>(input_rows_count, date1_col, 
date2_col, round_off_col,
+                                        *res);
+        } else if (col3_const) {
+            execute_vector<false, true>(input_rows_count, date1_col, 
date2_col, round_off_col,
+                                        *res);
+        } else {
+            execute_vector<false, false>(input_rows_count, date1_col, 
date2_col, round_off_col,
+                                         *res);
+        }
+
+        block.replace_by_position(result, std::move(res));
+        return Status::OK();
+    }
+
+private:
+    template <bool is_date_const, bool is_round_off_const>
+    static void execute_vector(const size_t input_rows_count, const 
ColumnDateV2& date1_col,
+                               const ColumnDateV2& date2_col, const 
ColumnBool& round_off_col,
+                               ColumnFloat64& res) {
+        res.reserve(input_rows_count);
+        double months_between;
+        bool round_off;
+
+        if constexpr (is_date_const) {
+            auto dtv1 = binary_cast<UInt32, 
DateV2Value<DateV2ValueType>>(date1_col.get_element(0));
+            auto dtv2 = binary_cast<UInt32, 
DateV2Value<DateV2ValueType>>(date2_col.get_element(0));
+            months_between = calc_months_between(dtv1, dtv2);
+        }
+
+        if constexpr (is_round_off_const) {
+            round_off = round_off_col.get_element(0);
+        }
+
+        for (int i = 0; i < input_rows_count; ++i) {
+            if constexpr (!is_date_const) {
+                auto dtv1 =
+                        binary_cast<UInt32, 
DateV2Value<DateV2ValueType>>(date1_col.get_element(i));
+                auto dtv2 =
+                        binary_cast<UInt32, 
DateV2Value<DateV2ValueType>>(date2_col.get_element(i));
+                months_between = calc_months_between(dtv1, dtv2);
+            }
+            if constexpr (!is_round_off_const) {
+                round_off = round_off_col.get_element(i);
+            }
+            if (round_off) {
+                months_between = round_months_between(months_between);
+            }
+            res.insert_value(months_between);
+        }
+    }
+
+    static double calc_months_between(const DateV2Value<DateV2ValueType>& dtv1,
+                                      const DateV2Value<DateV2ValueType>& 
dtv2) {
+        auto year_between = dtv1.year() - dtv2.year();
+        auto months_between = dtv1.month() - dtv2.month();
+        auto days_in_month1 = S_DAYS_IN_MONTH[dtv1.month()];
+        if (UNLIKELY(is_leap(dtv1.year()) && dtv1.month() == 2)) {
+            days_in_month1 = 29;
+        }
+        auto days_in_month2 = S_DAYS_IN_MONTH[dtv2.month()];
+        if (UNLIKELY(is_leap(dtv2.year()) && dtv2.month() == 2)) {
+            days_in_month2 = 29;
+        }
+        double days_between = 0;
+        // if date1 and date2 are all the last day of the month, days_between 
is 0
+        if (UNLIKELY(dtv1.day() == days_in_month1 && dtv2.day() == 
days_in_month2)) {
+            days_between = 0;
+        } else {
+            days_between = (dtv1.day() - dtv2.day()) / (double)31.0;
+        }
+
+        // calculate months between
+        return year_between * 12 + months_between + days_between;
+    }
+
+    static double round_months_between(double months_between) {
+        return round(months_between * 100000000) / 100000000;
+    }
+};
+
+class FunctionNextDay : public IFunction {
+public:
+    static constexpr auto name = "next_day";
+    static FunctionPtr create() { return std::make_shared<FunctionNextDay>(); }
+    String get_name() const override { return name; }
+    size_t get_number_of_arguments() const override { return 2; }
+    DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) 
const override {
+        return std::make_shared<DataTypeDateV2>();
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        CHECK_EQ(arguments.size(), 2);
+        auto res = ColumnDateV2::create();
+        res->reserve(input_rows_count);
+        const auto& [left_col, left_const] =
+                unpack_if_const(block.get_by_position(arguments[0]).column);
+        const auto& [right_col, right_const] =
+                unpack_if_const(block.get_by_position(arguments[1]).column);
+        const auto& date_col = *assert_cast<const 
ColumnDateV2*>(left_col.get());
+        const auto& week_col = *assert_cast<const 
ColumnString*>(right_col.get());
+        Status status;
+        if (left_const && right_const) {
+            status = execute_vector<true, true>(input_rows_count, date_col, 
week_col, *res);
+        } else if (left_const) {
+            status = execute_vector<true, false>(input_rows_count, date_col, 
week_col, *res);
+        } else if (right_const) {
+            status = execute_vector<false, true>(input_rows_count, date_col, 
week_col, *res);
+        } else {
+            status = execute_vector<false, false>(input_rows_count, date_col, 
week_col, *res);
+        }
+        if (!status.ok()) {
+            return status;
+        }
+        block.replace_by_position(result, std::move(res));
+        return Status::OK();
+    }
+
+private:
+    static int day_of_week(const StringRef& weekday) {
+        static const std::unordered_map<std::string, int> weekday_map = {
+                {"MO", 1}, {"MON", 1}, {"MONDAY", 1},    {"TU", 2}, {"TUE", 
2}, {"TUESDAY", 2},
+                {"WE", 3}, {"WED", 3}, {"WEDNESDAY", 3}, {"TH", 4}, {"THU", 
4}, {"THURSDAY", 4},
+                {"FR", 5}, {"FRI", 5}, {"FRIDAY", 5},    {"SA", 6}, {"SAT", 
6}, {"SATURDAY", 6},
+                {"SU", 7}, {"SUN", 7}, {"SUNDAY", 7}};
+        auto weekday_upper = weekday.to_string();
+        std::transform(weekday_upper.begin(), weekday_upper.end(), 
weekday_upper.begin(),
+                       ::toupper);
+        auto it = weekday_map.find(weekday_upper);
+        if (it == weekday_map.end()) {
+            return 0;
+        }
+        return it->second;
+    }
+    static Status compute_next_day(DateV2Value<DateV2ValueType>& dtv, const 
int week_day) {
+        auto days_to_add = (week_day - (dtv.weekday() + 1) + 7) % 7;
+        days_to_add = days_to_add == 0 ? 7 : days_to_add;
+        dtv.date_add_interval<TimeUnit::DAY>(TimeInterval(TimeUnit::DAY, 
days_to_add, false));
+        return Status::OK();
+    }
+
+    template <bool left_const, bool right_const>
+    static Status execute_vector(size_t input_rows_count, const ColumnDateV2& 
left_col,
+                                 const ColumnString& right_col, ColumnDateV2& 
res_col) {
+        DateV2Value<DateV2ValueType> dtv;
+        int week_day;
+        if constexpr (left_const) {
+            dtv = binary_cast<UInt32, 
DateV2Value<DateV2ValueType>>(left_col.get_element(0));
+        }
+        if constexpr (right_const) {
+            auto week = right_col.get_data_at(0);
+            week_day = day_of_week(week);
+            if (week_day == 0) {
+                return Status::InvalidArgument("Function {} failed to parse 
weekday: {}", name,
+                                               week);
+            }
+        }
+
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            if constexpr (!left_const) {
+                dtv = binary_cast<UInt32, 
DateV2Value<DateV2ValueType>>(left_col.get_element(i));
+            }
+            if constexpr (!right_const) {
+                auto week = right_col.get_data_at(i);
+                week_day = day_of_week(week);
+                if (week_day == 0) {
+                    return Status::InvalidArgument("Function {} failed to 
parse weekday: {}", name,
+                                                   week);
+                }
+            }
+            RETURN_IF_ERROR(compute_next_day(dtv, week_day));
+            res_col.insert_value(binary_cast<DateV2Value<DateV2ValueType>, 
UInt32>(dtv));
+        }
+        return Status::OK();
+    }
+};
+
 } // namespace doris::vectorized
diff --git a/be/src/vec/functions/function_map.cpp 
b/be/src/vec/functions/function_map.cpp
index d781fc6cac4..3a685608412 100644
--- a/be/src/vec/functions/function_map.cpp
+++ b/be/src/vec/functions/function_map.cpp
@@ -23,10 +23,12 @@
 #include <memory>
 #include <ostream>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <utility>
 
 #include "common/status.h"
+#include "util/simd/vstring_function.h"
 #include "vec/aggregate_functions/aggregate_function.h"
 #include "vec/columns/column.h"
 #include "vec/columns/column_array.h"
@@ -46,6 +48,7 @@
 #include "vec/data_types/data_type_map.h"
 #include "vec/data_types/data_type_nullable.h"
 #include "vec/data_types/data_type_number.h"
+#include "vec/data_types/data_type_string.h"
 #include "vec/functions/array/function_array_index.h"
 #include "vec/functions/function.h"
 #include "vec/functions/simple_function_factory.h"
@@ -286,12 +289,165 @@ public:
     }
 };
 
+class FunctionStrToMap : public IFunction {
+public:
+    static constexpr auto name = "str_to_map";
+    static FunctionPtr create() { return std::make_shared<FunctionStrToMap>(); 
}
+
+    String get_name() const override { return name; }
+
+    size_t get_number_of_arguments() const override { return 3; }
+
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        return 
std::make_shared<DataTypeMap>(make_nullable(std::make_shared<DataTypeString>()),
+                                             
make_nullable(std::make_shared<DataTypeString>()));
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        DCHECK(arguments.size() == 3);
+
+        bool cols_const[2];
+        ColumnPtr cols[2];
+        for (size_t i = 0; i < 2; ++i) {
+            cols_const[i] = 
is_column_const(*block.get_by_position(arguments[i]).column);
+        }
+        // convert to full column if necessary
+        default_preprocess_parameter_columns(cols, cols_const, {0, 1}, block, 
arguments);
+        const auto& [col3, col3_const] =
+                unpack_if_const(block.get_by_position(arguments[2]).column);
+
+        const auto& str_column = assert_cast<const 
ColumnString*>(cols[0].get());
+        const auto& pair_delim_column = assert_cast<const 
ColumnString*>(cols[1].get());
+        const auto& kv_delim_column = assert_cast<const 
ColumnString*>(col3.get());
+
+        ColumnPtr result_col;
+        if (cols_const[0] && cols_const[1]) {
+            result_col = execute_vector<true, false>(input_rows_count, 
*str_column,
+                                                     *pair_delim_column, 
*kv_delim_column);
+        } else if (col3_const) {
+            result_col = execute_vector<false, true>(input_rows_count, 
*str_column,
+                                                     *pair_delim_column, 
*kv_delim_column);
+        } else {
+            result_col = execute_vector<false, false>(input_rows_count, 
*str_column,
+                                                      *pair_delim_column, 
*kv_delim_column);
+        }
+
+        block.replace_by_position(result, std::move(result_col));
+
+        return Status::OK();
+    }
+
+private:
+    template <bool is_str_and_pair_delim_const, bool is_kv_delim_const>
+    static ColumnPtr execute_vector(const size_t input_rows_count, const 
ColumnString& str_col,
+                                    const ColumnString& pair_delim_col,
+                                    const ColumnString& kv_delim_col) {
+        // map keys column
+        auto result_col_map_keys_data =
+                ColumnNullable::create(ColumnString::create(), 
ColumnUInt8::create());
+        result_col_map_keys_data->reserve(input_rows_count);
+        // map values column
+        auto result_col_map_vals_data =
+                ColumnNullable::create(ColumnString::create(), 
ColumnUInt8::create());
+        result_col_map_vals_data->reserve(input_rows_count);
+        // map offsets column
+        auto result_col_map_offsets = ColumnUInt64::create();
+        result_col_map_offsets->reserve(input_rows_count);
+
+        std::vector<std::string_view> kvs;
+        std::string_view kv_delim;
+        if constexpr (is_str_and_pair_delim_const) {
+            auto str = str_col.get_data_at(0).to_string_view();
+            auto pair_delim = pair_delim_col.get_data_at(0).to_string_view();
+            kvs = split_pair_by_delim(str, pair_delim);
+        }
+        if constexpr (is_kv_delim_const) {
+            kv_delim = kv_delim_col.get_data_at(0).to_string_view();
+        }
+
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            if constexpr (!is_str_and_pair_delim_const) {
+                auto str = str_col.get_data_at(i).to_string_view();
+                auto pair_delim = 
pair_delim_col.get_data_at(i).to_string_view();
+                kvs = split_pair_by_delim(str, pair_delim);
+            }
+            if constexpr (!is_kv_delim_const) {
+                kv_delim = kv_delim_col.get_data_at(i).to_string_view();
+            }
+
+            for (const auto& kv : kvs) {
+                auto kv_parts = split_kv_by_delim(kv, kv_delim);
+                if (kv_parts.size() == 2) {
+                    result_col_map_keys_data->insert_data(kv_parts[0].data(), 
kv_parts[0].size());
+                    result_col_map_vals_data->insert_data(kv_parts[1].data(), 
kv_parts[1].size());
+                } else {
+                    result_col_map_keys_data->insert_data(kv.data(), 
kv.size());
+                    result_col_map_vals_data->insert_default();
+                }
+            }
+            
result_col_map_offsets->insert_value(result_col_map_keys_data->size());
+        }
+
+        return ColumnMap::create(std::move(result_col_map_keys_data),
+                                 std::move(result_col_map_vals_data),
+                                 std::move(result_col_map_offsets));
+    }
+
+    static std::vector<std::string_view> split_pair_by_delim(const 
std::string_view& str,
+                                                             const 
std::string_view& delim) {
+        if (str.empty()) {
+            return {str};
+        }
+        if (delim.empty()) {
+            std::vector<std::string_view> result;
+            size_t offset = 0;
+            while (offset < str.size()) {
+                auto len = get_utf8_byte_length(str[offset]);
+                result.push_back(str.substr(offset, len));
+                offset += len;
+            }
+            return result;
+        }
+        std::vector<std::string_view> result;
+        size_t offset = 0;
+        while (offset < str.size()) {
+            auto pos = str.find(delim, offset);
+            if (pos == std::string::npos) {
+                result.push_back(str.substr(offset));
+                break;
+            }
+            result.push_back(str.substr(offset, pos - offset));
+            offset = pos + delim.size();
+        }
+        return result;
+    }
+
+    static std::vector<std::string_view> split_kv_by_delim(const 
std::string_view& str,
+                                                           const 
std::string_view& delim) {
+        if (str.empty()) {
+            return {str};
+        }
+        if (delim.empty()) {
+            auto len = get_utf8_byte_length(str[0]);
+            return {str.substr(0, len), str.substr(len)};
+        }
+        auto pos = str.find(delim);
+        if (pos == std::string::npos) {
+            return {str};
+        } else {
+            return {str.substr(0, pos), str.substr(pos + delim.size())};
+        }
+    }
+};
+
 void register_function_map(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionMap>();
     factory.register_function<FunctionMapContains<true>>();
     factory.register_function<FunctionMapContains<false>>();
     factory.register_function<FunctionMapEntries<true>>();
     factory.register_function<FunctionMapEntries<false>>();
+    factory.register_function<FunctionStrToMap>();
 }
 
 } // namespace doris::vectorized
diff --git a/be/src/vec/functions/function_string.cpp 
b/be/src/vec/functions/function_string.cpp
index 064b3a7af22..babb3730ea8 100644
--- a/be/src/vec/functions/function_string.cpp
+++ b/be/src/vec/functions/function_string.cpp
@@ -1367,6 +1367,7 @@ void register_function_string(SimpleFunctionFactory& 
factory) {
     factory.register_function<FunctionOverlay>();
     factory.register_function<FunctionStrcmp>();
     factory.register_function<FunctionNgramSearch>();
+    factory.register_function<FunctionXPathString>();
 
     factory.register_alias(FunctionLeft::name, "strleft");
     factory.register_alias(FunctionRight::name, "strright");
diff --git a/be/src/vec/functions/function_string.h 
b/be/src/vec/functions/function_string.h
index 7202d356523..493d1549c4f 100644
--- a/be/src/vec/functions/function_string.h
+++ b/be/src/vec/functions/function_string.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <glog/logging.h>
 #include <sys/types.h>
 
 #include <algorithm>
@@ -85,6 +86,7 @@
 #include <string_view>
 
 #include "exprs/math_functions.h"
+#include "pugixml.hpp"
 #include "udf/udf.h"
 #include "util/md5.h"
 #include "util/simd/vstring_function.h"
@@ -4837,4 +4839,138 @@ private:
     }
 };
 
+/// xpath_string(xml, xpath) -> String
+/// Returns the text content of the first node that matches the XPath 
expression.
+/// Returns NULL if either xml or xpath is NULL.
+/// Returns empty string if the XPath expression matches no nodes.
+/// The text content includes the node and all its descendants.
+/// Example:
+///   xpath_string('<a><b>b1</b><b>b2</b></a>', '/a/b[1]') = 'b1'
+///   xpath_string('<a><b>b1</b><b>b2</b></a>', '/a/b[2]') = 'b2'
+///   xpath_string('<a><b>b1</b><b>b2</b></a>', '/a/c') = ''
+///   xpath_string('invalid xml', '/a/b[1]') = NULL
+///   xpath_string(NULL, '/a/b[1]') = NULL
+///   xpath_string('<a><b>b1</b><b>b2</b></a>', NULL) = NULL
+class FunctionXPathString : public IFunction {
+public:
+    static constexpr auto name = "xpath_string";
+    static FunctionPtr create() { return 
std::make_shared<FunctionXPathString>(); }
+    String get_name() const override { return name; }
+    size_t get_number_of_arguments() const override { return 2; }
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        return make_nullable(std::make_shared<DataTypeString>());
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        CHECK_EQ(arguments.size(), 2);
+        auto col_res = ColumnNullable::create(ColumnString::create(), 
ColumnUInt8::create());
+        const auto& [left_col, left_const] =
+                unpack_if_const(block.get_by_position(arguments[0]).column);
+        const auto& [right_col, right_const] =
+                unpack_if_const(block.get_by_position(arguments[1]).column);
+        const auto& xml_col = *assert_cast<const 
ColumnString*>(left_col.get());
+        const auto& xpath_col = *assert_cast<const 
ColumnString*>(right_col.get());
+
+        Status status;
+        if (left_const && right_const) {
+            status = execute_vector<true, true>(input_rows_count, xml_col, 
xpath_col, *col_res);
+        } else if (left_const) {
+            status = execute_vector<true, false>(input_rows_count, xml_col, 
xpath_col, *col_res);
+        } else if (right_const) {
+            status = execute_vector<false, true>(input_rows_count, xml_col, 
xpath_col, *col_res);
+        } else {
+            status = execute_vector<false, false>(input_rows_count, xml_col, 
xpath_col, *col_res);
+        }
+        if (!status.ok()) {
+            return status;
+        }
+
+        block.get_by_position(result).column = std::move(col_res);
+        return Status::OK();
+    }
+
+private:
+    // Build the text of the node and all its children.
+    static std::string get_text(const pugi::xml_node& node) {
+        std::string result;
+        build_text(node, result);
+        return result;
+    }
+
+    static void build_text(const pugi::xml_node& node, std::string& builder) {
+        if (node.type() == pugi::node_pcdata || node.type() == 
pugi::node_cdata) {
+            builder += node.value();
+        }
+        for (pugi::xml_node child : node.children()) {
+            build_text(child, builder);
+        }
+    }
+
+    static Status parse_xml(const StringRef& xml_str, pugi::xml_document& 
xml_doc) {
+        pugi::xml_parse_result result = xml_doc.load_buffer(xml_str.data, 
xml_str.size);
+        if (!result) {
+            return Status::InvalidArgument("Function {} failed to parse XML 
string: {}", name,
+                                           result.description());
+        }
+        return Status::OK();
+    }
+
+    template <bool left_const, bool right_const>
+    static Status execute_vector(const size_t input_rows_count, const 
ColumnString& xml_col,
+                                 const ColumnString& xpath_col, 
ColumnNullable& res_col) {
+        pugi::xml_document xml_doc;
+        StringRef xpath_str;
+        // first check right_const, because we want to check empty input first
+        if constexpr (right_const) {
+            xpath_str = xpath_col.get_data_at(0);
+            if (xpath_str.empty()) {
+                // should return null if xpath_str is empty
+                res_col.insert_many_defaults(input_rows_count);
+                return Status::OK();
+            }
+        }
+        if constexpr (left_const) {
+            auto xml_str = xml_col.get_data_at(0);
+            if (xml_str.empty()) {
+                // should return null if xml_str is empty
+                res_col.insert_many_defaults(input_rows_count);
+                return Status::OK();
+            }
+            RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
+        }
+
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            if constexpr (!right_const) {
+                xpath_str = xpath_col.get_data_at(i);
+                if (xpath_str.empty()) {
+                    // should return null if xpath_str is empty
+                    res_col.insert_default();
+                    continue;
+                }
+            }
+            if constexpr (!left_const) {
+                auto xml_str = xml_col.get_data_at(i);
+                if (xml_str.empty()) {
+                    // should return null if xml_str is empty
+                    res_col.insert_default();
+                    continue;
+                }
+                RETURN_IF_ERROR(parse_xml(xml_str, xml_doc));
+            }
+            // NOTE!!!: don't use to_string_view(), because xpath_str maybe 
not null-terminated
+            pugi::xpath_node node = 
xml_doc.select_node(xpath_str.to_string().c_str());
+            if (!node) {
+                // should return empty string if not found
+                auto empty_str = std::string("");
+                res_col.insert_data(empty_str.data(), empty_str.size());
+                continue;
+            }
+            auto text = get_text(node.node());
+            res_col.insert_data(text.data(), text.size());
+        }
+        return Status::OK();
+    }
+};
+
 } // namespace doris::vectorized
diff --git a/be/test/vec/function/function_string_test.cpp 
b/be/test/vec/function/function_string_test.cpp
index 9f1ffe438a1..8c4b3c8a1c7 100644
--- a/be/test/vec/function/function_string_test.cpp
+++ b/be/test/vec/function/function_string_test.cpp
@@ -3372,4 +3372,55 @@ TEST(function_string_test, function_rpad_test) {
     check_function_all_arg_comb<DataTypeString, true>(func_name, input_types, 
data_set);
 }
 
+TEST(function_string_test, function_xpath_string_test) {
+    std::string func_name = "xpath_string";
+    BaseInputTypeSet input_types = {TypeIndex::String, TypeIndex::String};
+
+    DataSet data_set = {
+            {{std::string("<a>123</a>"), std::string("/a")}, 
std::string("123")},
+            {{std::string("<a><b>123</b></a>"), std::string("/a/b")}, 
std::string("123")},
+            {{std::string("<a><b>123</b><c>456</c></a>"), 
std::string("/a/c")}, std::string("456")},
+            {{std::string("<a><b>123</b><c>456</c></a>"), 
std::string("/a/d")}, std::string("")},
+            {{std::string("<a><b>123</b><b>456</b></a>"), 
std::string("/a/b[1]")},
+             std::string("123")},
+            {{std::string("<a><b>123</b><b>456</b></a>"), 
std::string("/a/b[2]")},
+             std::string("456")},
+            {{std::string("<a><b>123</b><b>456</b></a>"), 
std::string("/a/b[3]")}, std::string("")},
+            {{std::string("<a><b attr='val'>123</b></a>"), 
std::string("/a/b[@attr]")},
+             std::string("123")},
+            {{std::string("<a><b attr='val'>123</b></a>"), 
std::string("/a/b[@attr='val']")},
+             std::string("123")},
+            {{std::string("<a><b attr='val'>123</b></a>"), 
std::string("/a/b[@attr='wrong']")},
+             std::string("")},
+            {{std::string("<a><!-- comment -->123</a>"), std::string("/a")}, 
std::string("123")},
+            {{std::string("<a><![CDATA[123]]></a>"), std::string("/a")}, 
std::string("123")},
+            {{std::string("<a>123<b>456</b>789</a>"), std::string("/a")}, 
std::string("123456789")},
+            {{std::string("<a>  123  </a>"), std::string("/a")}, std::string(" 
 123  ")},
+            {{std::string("<a></a>"), std::string("/a")}, std::string("")},
+            {{std::string("<a/>"), std::string("/a")}, std::string("")},
+            {{std::string("<a>123</a>"), std::string("")}, Null()},
+            {{std::string(""), std::string("/a")}, Null()},
+            {{Null(), std::string("/a")}, Null()},
+            {{std::string("<a>123</a>"), Null()}, Null()},
+            {{std::string("<book><title>Intro to Hive</title><author>John "
+                          "Doe</author><publisher>Tech 
Press</publisher></book>"),
+              std::string("//title/text()")},
+             std::string("Intro to Hive")},
+            {{std::string("<book><title>Intro to Hive</title><author>John "
+                          "Doe</author><publisher>Tech 
Press</publisher></book>"),
+              std::string("//author/text()")},
+             std::string("John Doe")},
+            {{std::string("<book><title>Intro to Hive</title><author>John "
+                          "Doe</author><publisher>Tech 
Press</publisher></book>"),
+              std::string("//publisher/text()")},
+             std::string("Tech Press")},
+            {{std::string("<book><title>Intro to Hive</title><author>John "
+                          "Doe</author><publisher>Tech 
Press</publisher></book>"),
+              std::string("/book")},
+             std::string("Intro to HiveJohn DoeTech Press")},
+            {{Null(), Null()}, Null()}};
+
+    check_function_all_arg_comb<DataTypeString, true>(func_name, input_types, 
data_set);
+}
+
 } // namespace doris::vectorized
diff --git a/be/test/vec/function/function_time_test.cpp 
b/be/test/vec/function/function_time_test.cpp
index 17f487287ec..1ea010e4afd 100644
--- a/be/test/vec/function/function_time_test.cpp
+++ b/be/test/vec/function/function_time_test.cpp
@@ -1785,4 +1785,111 @@ TEST(VTimestampFunctionsTest, year_of_week_test) {
     }
 }
 
+TEST(VTimestampFunctionsTest, months_between_test) {
+    std::string func_name = "months_between";
+    BaseInputTypeSet input_types = {TypeIndex::DateV2, TypeIndex::DateV2, 
TypeIndex::UInt8};
+    DataSet data_set = {
+            {{std::string("2020-01-01"), std::string("2020-02-01"), 
uint8_t(0)}, double(-1.0)},
+            {{std::string("2020-01-01"), std::string("2020-03-01"), 
uint8_t(1)}, double(-2.0)},
+            {{std::string("2020-01-01"), std::string("2020-04-01"), 
uint8_t(0)}, double(-3.0)},
+            {{std::string("2020-01-01"), std::string("2020-12-01"), 
uint8_t(1)}, double(-11.0)},
+            {{std::string("2020-01-01"), std::string("2021-01-01"), 
uint8_t(0)}, double(-12.0)},
+            {{std::string("2020-01-01"), std::string("2022-01-01"), 
uint8_t(1)}, double(-24.0)},
+            {{std::string("2020-01-01"), std::string("2020-01-01"), 
uint8_t(0)}, double(0.0)},
+            {{std::string("2020-12-01"), std::string("2020-01-01"), 
uint8_t(1)}, double(11.0)},
+            {{std::string("2021-01-01"), std::string("2020-01-01"), 
uint8_t(0)}, double(12.0)},
+            {{std::string("2022-01-01"), std::string("2020-01-01"), 
uint8_t(1)}, double(24.0)},
+            {{std::string(""), std::string("2020-01-01"), uint8_t(1)}, Null()},
+            {{std::string("2020-01-01"), std::string(""), uint8_t(1)}, Null()},
+            {{Null(), std::string("2020-01-01"), uint8_t(1)}, Null()},
+            {{std::string("2020-01-01"), Null(), uint8_t(1)}, Null()}};
+    static_cast<void>(
+            check_function_all_arg_comb<DataTypeFloat64, true>(func_name, 
input_types, data_set));
+}
+
+TEST(VTimestampFunctionsTest, next_day_test) {
+    std::string func_name = "next_day";
+    BaseInputTypeSet input_types = {TypeIndex::DateV2, TypeIndex::String};
+    {
+        DataSet data_set = {{{std::string("2020-01-01"), std::string("MO")},
+                             str_to_date_v2("2020-01-06", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("MON")},
+                             str_to_date_v2("2020-01-06", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("MONDAY")},
+                             str_to_date_v2("2020-01-06", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("TU")},
+                             str_to_date_v2("2020-01-07", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("TUE")},
+                             str_to_date_v2("2020-01-07", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("TUESDAY")},
+                             str_to_date_v2("2020-01-07", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("WE")},
+                             str_to_date_v2("2020-01-08", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("WED")},
+                             str_to_date_v2("2020-01-08", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("WEDNESDAY")},
+                             str_to_date_v2("2020-01-08", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("TH")},
+                             str_to_date_v2("2020-01-02", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("THU")},
+                             str_to_date_v2("2020-01-02", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("THURSDAY")},
+                             str_to_date_v2("2020-01-02", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("FR")},
+                             str_to_date_v2("2020-01-03", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("FRI")},
+                             str_to_date_v2("2020-01-03", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("FRIDAY")},
+                             str_to_date_v2("2020-01-03", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("SA")},
+                             str_to_date_v2("2020-01-04", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("SAT")},
+                             str_to_date_v2("2020-01-04", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("SATURDAY")},
+                             str_to_date_v2("2020-01-04", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("SU")},
+                             str_to_date_v2("2020-01-05", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), std::string("SUN")},
+                             str_to_date_v2("2020-01-05", "%Y-%m-%d")},
+                            {{std::string("2020-01-01"), 
std::string("SUNDAY")},
+                             str_to_date_v2("2020-01-05", "%Y-%m-%d")},
+                            {{std::string(""), std::string("MON")}, Null()},
+                            {{Null(), std::string("MON")}, Null()}};
+        static_cast<void>(check_function_all_arg_comb<DataTypeDateV2, 
true>(func_name, input_types,
+                                                                            
data_set));
+    }
+    {
+        DataSet data_set = {// date over month
+                            {{std::string("2020-01-28"), std::string("MON")},
+                             str_to_date_v2("2020-02-03", "%Y-%m-%d")},
+                            {{std::string("2020-01-31"), std::string("SAT")},
+                             str_to_date_v2("2020-02-01", "%Y-%m-%d")},
+
+                            // date over year
+                            {{std::string("2020-12-28"), std::string("FRI")},
+                             str_to_date_v2("2021-01-01", "%Y-%m-%d")},
+                            {{std::string("2020-12-31"), std::string("THU")},
+                             str_to_date_v2("2021-01-07", "%Y-%m-%d")},
+
+                            // leap year(29 Feb)
+                            {{std::string("2020-02-27"), std::string("SAT")},
+                             str_to_date_v2("2020-02-29", "%Y-%m-%d")},
+                            {{std::string("2020-02-29"), std::string("MON")},
+                             str_to_date_v2("2020-03-02", "%Y-%m-%d")},
+
+                            // non leap year(28 Feb)
+                            {{std::string("2019-02-26"), std::string("THU")},
+                             str_to_date_v2("2019-02-28", "%Y-%m-%d")},
+                            {{std::string("2019-02-28"), std::string("SUN")},
+                             str_to_date_v2("2019-03-03", "%Y-%m-%d")},
+
+                            // date over month
+                            {{std::string("2020-04-29"), std::string("FRI")},
+                             str_to_date_v2("2020-05-01", "%Y-%m-%d")},
+                            {{std::string("2020-05-31"), std::string("MON")},
+                             str_to_date_v2("2020-06-01", "%Y-%m-%d")}};
+        static_cast<void>(check_function_all_arg_comb<DataTypeDateV2, 
true>(func_name, input_types,
+                                                                            
data_set));
+    }
+}
 } // namespace doris::vectorized
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 8452c5ad358..1c1a820952f 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
@@ -315,6 +315,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthFloor;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthName;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsAdd;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsBetween;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsDiff;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsSub;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MultiMatch;
@@ -323,6 +324,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MultiSearchAl
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MurmurHash332;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MurmurHash364;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Negative;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.NextDay;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.NgramSearch;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.NonNullable;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.NormalCdf;
@@ -414,6 +416,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StX;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StY;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToMap;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Strcmp;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap;
@@ -468,6 +471,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksSub;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.WidthBucket;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Xor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.XpathString;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.XxHash32;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.XxHash64;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Year;
@@ -802,6 +806,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(MonthFloor.class, "month_floor"),
             scalar(MonthName.class, "monthname"),
             scalar(MonthsAdd.class, "months_add", "add_months"),
+            scalar(MonthsBetween.class, "months_between"),
             scalar(MonthsDiff.class, "months_diff"),
             scalar(MonthsSub.class, "months_sub"),
             scalar(MultiMatchAny.class, "multi_match_any"),
@@ -809,6 +814,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(MurmurHash332.class, "murmur_hash3_32"),
             scalar(MurmurHash364.class, "murmur_hash3_64"),
             scalar(Negative.class, "negative"),
+            scalar(NextDay.class, "next_day"),
             scalar(NonNullable.class, "non_nullable"),
             scalar(NormalCdf.class, "normal_cdf"),
             scalar(NotNullOrEmpty.class, "not_null_or_empty"),
@@ -907,6 +913,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(StartsWith.class, "starts_with"),
             scalar(Strcmp.class, "strcmp"),
             scalar(StrToDate.class, "str_to_date"),
+            scalar(StrToMap.class, "str_to_map"),
             scalar(SubBitmap.class, "sub_bitmap"),
             scalar(SubReplace.class, "sub_replace"),
             scalar(Substring.class, "substr", "substring"),
@@ -962,6 +969,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(XxHash32.class, "xxhash_32"),
             scalar(XxHash64.class, "xxhash_64"),
             scalar(Xor.class, "xor"),
+            scalar(XpathString.class, "xpath_string"),
             scalar(Year.class, "year"),
             scalar(YearCeil.class, "year_ceil"),
             scalar(YearFloor.class, "year_floor"),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
index 0be185d80f9..568740f9d35 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
@@ -24,15 +24,18 @@ import 
org.apache.doris.nereids.rules.expression.rules.SupportJavaDateFormatter;
 import org.apache.doris.nereids.trees.expressions.ExecFunction;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
 import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalV3Literal;
+import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
 import org.apache.doris.nereids.types.DataType;
@@ -44,11 +47,13 @@ import org.apache.doris.nereids.types.VarcharType;
 import org.apache.doris.nereids.util.DateUtils;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.DayOfWeek;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.YearMonth;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
@@ -58,6 +63,7 @@ import java.time.format.ResolverStyle;
 import java.time.format.TextStyle;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.WeekFields;
+import java.util.HashMap;
 import java.util.Locale;
 
 /**
@@ -65,6 +71,33 @@ import java.util.Locale;
  * year, quarter, month, week, dayOfYear, dayOfweek, dayOfMonth, hour, minute, 
second, microsecond
  */
 public class DateTimeExtractAndTransform {
+
+    private static final HashMap<String, Integer> DAY_OF_WEEK = new 
HashMap<>();
+
+    static {
+        DAY_OF_WEEK.put("MO", 1);
+        DAY_OF_WEEK.put("MON", 1);
+        DAY_OF_WEEK.put("MONDAY", 1);
+        DAY_OF_WEEK.put("TU", 2);
+        DAY_OF_WEEK.put("TUE", 2);
+        DAY_OF_WEEK.put("TUESDAY", 2);
+        DAY_OF_WEEK.put("WE", 3);
+        DAY_OF_WEEK.put("WED", 3);
+        DAY_OF_WEEK.put("WEDNESDAY", 3);
+        DAY_OF_WEEK.put("TH", 4);
+        DAY_OF_WEEK.put("THU", 4);
+        DAY_OF_WEEK.put("THURSDAY", 4);
+        DAY_OF_WEEK.put("FR", 5);
+        DAY_OF_WEEK.put("FRI", 5);
+        DAY_OF_WEEK.put("FRIDAY", 5);
+        DAY_OF_WEEK.put("SA", 6);
+        DAY_OF_WEEK.put("SAT", 6);
+        DAY_OF_WEEK.put("SATURDAY", 6);
+        DAY_OF_WEEK.put("SU", 7);
+        DAY_OF_WEEK.put("SUN", 7);
+        DAY_OF_WEEK.put("SUNDAY", 7);
+    }
+
     /**
      * datetime arithmetic function date-v2
      */
@@ -1185,4 +1218,49 @@ public class DateTimeExtractAndTransform {
     public static Expression yearsDiff(DateTimeLiteral t1, DateTimeLiteral t2) 
{
         return new BigIntLiteral(ChronoUnit.YEARS.between(t2.toJavaDateType(), 
t1.toJavaDateType()));
     }
+
+    /**
+     * months_between(date1, date2, round_off)
+     */
+    @ExecFunction(name = "months_between")
+    public static Expression monthsBetween(DateV2Literal t1, DateV2Literal t2, 
BooleanLiteral roundOff) {
+        long yearBetween = t1.getYear() - t2.getYear();
+        long monthBetween = t1.getMonth() - t2.getMonth();
+        int daysInMonth1 = YearMonth.of((int) t1.getYear(), (int) 
t1.getMonth()).lengthOfMonth();
+        int daysInMonth2 = YearMonth.of((int) t2.getYear(), (int) 
t2.getMonth()).lengthOfMonth();
+        double dayBetween = 0;
+        if (t1.getDay() == daysInMonth1 && t2.getDay() == daysInMonth2) {
+            dayBetween = 0;
+        } else {
+            dayBetween = (t1.getDay() - t2.getDay()) / 31.0;
+        }
+        double result = yearBetween * 12 + monthBetween + dayBetween;
+        // rounded to 8 digits unless roundOff=false.
+        if (roundOff.getValue()) {
+            result = new BigDecimal(result).setScale(8, 
RoundingMode.HALF_UP).doubleValue();
+        }
+        return new DoubleLiteral(result);
+    }
+
+    private static int getDayOfWeek(String day) {
+        Integer dayOfWeek = DAY_OF_WEEK.get(day.toUpperCase());
+        if (dayOfWeek == null) {
+            return 0;
+        }
+        return dayOfWeek;
+    }
+
+    /**
+     * date arithmetic function next_day
+     */
+    @ExecFunction(name = "next_day")
+    public static Expression nextDay(DateV2Literal date, StringLiteral day) {
+        int dayOfWeek = getDayOfWeek(day.getValue());
+        if (dayOfWeek == 0) {
+            throw new RuntimeException("Invalid day of week: " + 
day.getValue());
+        }
+        int daysToAdd = (dayOfWeek - date.getDayOfWeek() + 7) % 7;
+        daysToAdd = daysToAdd == 0 ? 7 : daysToAdd;
+        return date.plusDays(daysToAdd);
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsBetween.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsBetween.java
new file mode 100644
index 00000000000..81d451602d8
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthsBetween.java
@@ -0,0 +1,83 @@
+// 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.PropagateNullableOnDateLikeV2Args;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.BooleanType;
+import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.DoubleType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'months_between'.
+ */
+public class MonthsBetween extends ScalarFunction
+        implements TernaryExpression, ExplicitlyCastableSignature, 
PropagateNullableOnDateLikeV2Args {
+
+    private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(DoubleType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE,
+                    BooleanType.INSTANCE),
+            
FunctionSignature.ret(DoubleType.INSTANCE).args(DateV2Type.INSTANCE, 
DateV2Type.INSTANCE));
+
+    /**
+     * constructor with 2 arguments.
+     */
+    public MonthsBetween(Expression arg0, Expression arg1) {
+        super("months_between", arg0, arg1, Literal.of(true));
+    }
+
+    /**
+     * constructor with 3 arguments.
+     */
+    public MonthsBetween(Expression arg0, Expression arg1, Expression arg2) {
+        super("months_between", arg0, arg1, arg2);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public MonthsBetween withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2 || children.size() == 
3);
+        if (children.size() == 2) {
+            return new MonthsBetween(children.get(0), children.get(1));
+        } else {
+            return new MonthsBetween(children.get(0), children.get(1), 
children.get(2));
+        }
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitMonthsBetween(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NextDay.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NextDay.java
new file mode 100644
index 00000000000..7e30e4ffd24
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NextDay.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.PropagateNullableOnDateLikeV2Args;
+import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.DateV2Type;
+import org.apache.doris.nereids.types.StringType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * ScalarFunction 'next_day'.
+ * next_day(expr, dayOfWeek)
+ * - expr: A DATE expression.
+ * - dayOfWeek: A STRING expression identifying a day of the week.
+ * Returns the first DATE that is later than expr and has the same day of the
+ * week as dayOfWeek.
+ * dayOfWeek must be one of the following (case insensitive):
+ * 'SU', 'SUN', 'SUNDAY'
+ * 'MO', 'MON', 'MONDAY'
+ * 'TU', 'TUE', 'TUESDAY'
+ * 'WE', 'WED', 'WEDNESDAY'
+ * 'TH', 'THU', 'THURSDAY'
+ * 'FR', 'FRI', 'FRIDAY'
+ * 'SA', 'SAT', 'SATURDAY'
+ */
+public class NextDay extends ScalarFunction
+        implements BinaryExpression, ExplicitlyCastableSignature, 
PropagateNullableOnDateLikeV2Args {
+    private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            
FunctionSignature.ret(DateV2Type.INSTANCE).args(DateV2Type.INSTANCE, 
StringType.INSTANCE));
+
+    public NextDay(Expression arg0, Expression arg1) {
+        super("next_day", arg0, arg1);
+    }
+
+    @Override
+    public NextDay withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new NextDay(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.visitNextDay(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToMap.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToMap.java
new file mode 100644
index 00000000000..89df45d01c0
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToMap.java
@@ -0,0 +1,110 @@
+// 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.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.MapType;
+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 'str_to_map'.
+ * str_to_map(expr [, pairDelim [, keyValueDelim] ] )
+ * - expr: An STRING expression.
+ * - pairDelim: An optional STRING literal defaulting to ',' that specifies how
+ * to split entries.
+ * - keyValueDelim: An optional STRING literal defaulting to ':' that specifies
+ * how to split each key-value pair.
+ * Returns:
+ * A MAP of STRING for both keys and values.
+ * Both pairDelim and keyValueDelim are treated as regular expressions.
+ */
+public class StrToMap extends ScalarFunction
+        implements ExplicitlyCastableSignature, PropagateNullable {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(MapType.of(StringType.INSTANCE, 
StringType.INSTANCE))
+                    .args(VarcharType.SYSTEM_DEFAULT),
+            FunctionSignature.ret(MapType.of(StringType.INSTANCE, 
StringType.INSTANCE)).args(VarcharType.SYSTEM_DEFAULT,
+                    VarcharType.SYSTEM_DEFAULT),
+            FunctionSignature.ret(MapType.of(StringType.INSTANCE, 
StringType.INSTANCE)).args(VarcharType.SYSTEM_DEFAULT,
+                    VarcharType.SYSTEM_DEFAULT,
+                    VarcharType.SYSTEM_DEFAULT),
+            FunctionSignature.ret(MapType.of(StringType.INSTANCE, 
StringType.INSTANCE)).args(StringType.INSTANCE),
+            FunctionSignature.ret(MapType.of(StringType.INSTANCE, 
StringType.INSTANCE)).args(StringType.INSTANCE,
+                    StringType.INSTANCE),
+            FunctionSignature.ret(MapType.of(StringType.INSTANCE, 
StringType.INSTANCE)).args(StringType.INSTANCE,
+                    StringType.INSTANCE,
+                    StringType.INSTANCE));
+
+    /**
+     * constructor with 1 arguments.
+     */
+    public StrToMap(Expression arg0) {
+        super("str_to_map", arg0, Literal.of(","), Literal.of(":"));
+    }
+
+    /**
+     * constructor with 2 arguments.
+     */
+    public StrToMap(Expression arg0, Expression arg1) {
+        super("str_to_map", arg0, arg1, Literal.of(":"));
+    }
+
+    /**
+     * constructor with 3 arguments.
+     */
+    public StrToMap(Expression arg0, Expression arg1, Expression arg2) {
+        super("str_to_map", arg0, arg1, arg2);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public StrToMap withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1 || children.size() == 
2 || children.size() == 3);
+        if (children.size() == 1) {
+            return new StrToMap(children.get(0));
+        } else if (children.size() == 2) {
+            return new StrToMap(children.get(0), children.get(1));
+        } else {
+            return new StrToMap(children.get(0), children.get(1), 
children.get(2));
+        }
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitStrToMap(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/XpathString.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/XpathString.java
new file mode 100644
index 00000000000..734af357d71
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/XpathString.java
@@ -0,0 +1,72 @@
+// 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.shape.BinaryExpression;
+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 'xpath_string'.
+ */
+public class XpathString extends ScalarFunction
+        implements BinaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT)
+                    .args(VarcharType.SYSTEM_DEFAULT, 
VarcharType.SYSTEM_DEFAULT),
+            FunctionSignature.ret(StringType.INSTANCE)
+                    .args(StringType.INSTANCE, StringType.INSTANCE)
+    );
+
+    /**
+     * constructor with 2 arguments.
+     */
+    public XpathString(Expression arg0, Expression arg1) {
+        super("xpath_string", arg0, arg1);
+    }
+
+    /**
+     * withChildren.
+     */
+    @Override
+    public XpathString withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new XpathString(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.visitXpathString(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 2890787ddb7..b061e2f8d6a 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
@@ -316,6 +316,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthFloor;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthName;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsAdd;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsBetween;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsDiff;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsSub;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MultiMatch;
@@ -324,6 +325,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MultiSearchAl
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MurmurHash332;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MurmurHash364;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Negative;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.NextDay;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.NgramSearch;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.NormalCdf;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.NotNullOrEmpty;
@@ -412,6 +414,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StX;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StY;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToMap;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Strcmp;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap;
@@ -465,6 +468,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksDiff;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.WeeksSub;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.WidthBucket;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Xor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.XpathString;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.XxHash32;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.XxHash64;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Year;
@@ -1084,6 +1088,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(monthsAdd, context);
     }
 
+    default R visitMonthsBetween(MonthsBetween monthsBetween, C context) {
+        return visitScalarFunction(monthsBetween, context);
+    }
+
     default R visitYearsAdd(YearsAdd yearsAdd, C context) {
         return visitScalarFunction(yearsAdd, context);
     }
@@ -1668,6 +1676,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(negative, context);
     }
 
+    default R visitNextDay(NextDay nextDay, C context) {
+        return visitScalarFunction(nextDay, context);
+    }
+
     default R visitNgramSearch(NgramSearch ngramSearch, C context) {
         return visitScalarFunction(ngramSearch, context);
     }
@@ -2024,6 +2036,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(strToDate, context);
     }
 
+    default R visitStrToMap(StrToMap strToMap, C context) {
+        return visitScalarFunction(strToMap, context);
+    }
+
     default R visitStringRegexPredicate(StringRegexPredicate 
stringRegexPredicate, C context) {
         return visitScalarFunction(stringRegexPredicate, context);
     }
@@ -2302,6 +2318,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(xor, context);
     }
 
+    default R visitXpathString(XpathString xpathString, C context) {
+        return visitScalarFunction(xpathString, context);
+    }
+
     // struct function
 
     default R visitCreateStruct(CreateStruct createStruct, C context) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
index b8de53b78fe..55c7b279cd3 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java
@@ -55,6 +55,8 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ln;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Locate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Log;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.MinutesAdd;
+import 
org.apache.doris.nereids.trees.expressions.functions.scalar.MonthsBetween;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.NextDay;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Overlay;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Power;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.ReplaceEmpty;
@@ -70,6 +72,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Tan;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.ToDays;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.UnixTimestamp;
 import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
@@ -276,6 +279,60 @@ class FoldConstantTest extends ExpressionRewriteTestHelper 
{
         toDays = new 
ToDays(DateV2Literal.fromJavaDateType(LocalDateTime.of(9999, 12, 31, 1, 1, 1)));
         rewritten = executor.rewrite(toDays, context);
         Assertions.assertEquals(new IntegerLiteral(3652424), rewritten);
+
+        MonthsBetween monthsBetween = new MonthsBetween(
+                        DateV2Literal.fromJavaDateType(LocalDateTime.of(1, 1, 
1, 1, 1, 1)),
+                        DateV2Literal.fromJavaDateType(LocalDateTime.of(2, 2, 
1, 1, 1, 1)));
+        rewritten = executor.rewrite(monthsBetween, context);
+        Assertions.assertEquals(new DoubleLiteral(-13.0), rewritten);
+        monthsBetween = new 
MonthsBetween(DateV2Literal.fromJavaDateType(LocalDateTime.of(1, 1, 1, 1, 1, 
1)),
+                        DateV2Literal.fromJavaDateType(LocalDateTime.of(2, 2, 
1, 1, 1, 1)), BooleanLiteral.FALSE);
+        rewritten = executor.rewrite(monthsBetween, context);
+        Assertions.assertEquals(new DoubleLiteral(-13.0), rewritten);
+        monthsBetween = new 
MonthsBetween(DateV2Literal.fromJavaDateType(LocalDateTime.of(2024, 3, 31, 1, 
1, 1)),
+                        DateV2Literal.fromJavaDateType(LocalDateTime.of(2024, 
2, 29, 1, 1, 1)));
+        rewritten = executor.rewrite(monthsBetween, context);
+        Assertions.assertEquals(new DoubleLiteral(1.0), rewritten);
+        NextDay nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 1, 28, 1, 1, 1)),
+                        StringLiteral.of("MON"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2020-02-03"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 1, 31, 1, 1, 1)),
+                        StringLiteral.of("SAT"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2020-02-01"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 12, 28, 1, 1, 1)),
+                        StringLiteral.of("FRI"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2021-01-01"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 12, 31, 1, 1, 1)),
+                        StringLiteral.of("THU"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2021-01-07"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 2, 27, 1, 1, 1)),
+                        StringLiteral.of("SAT"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2020-02-29"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 2, 29, 1, 1, 1)),
+                        StringLiteral.of("MON"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2020-03-02"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2019, 2, 26, 1, 1, 1)),
+                        StringLiteral.of("THU"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2019-02-28"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2019, 2, 28, 1, 1, 1)),
+                        StringLiteral.of("SUN"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2019-03-03"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 4, 29, 1, 1, 1)),
+                        StringLiteral.of("FRI"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2020-05-01"), rewritten);
+        nextDay = new 
NextDay(DateV2Literal.fromJavaDateType(LocalDateTime.of(2020, 5, 31, 1, 1, 1)),
+                        StringLiteral.of("MON"));
+        rewritten = executor.rewrite(nextDay, context);
+        Assertions.assertEquals(new DateV2Literal("2020-06-01"), rewritten);
     }
 
     @Test
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
new file mode 100644
index 00000000000..0fddb567d46
Binary files /dev/null and 
b/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
 differ
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
new file mode 100644
index 00000000000..8bdb8678993
Binary files /dev/null and 
b/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
 differ
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_str_to_map.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_str_to_map.out
new file mode 100644
index 00000000000..6dd44129806
Binary files /dev/null and 
b/regression-test/data/query_p0/sql_functions/string_functions/test_str_to_map.out
 differ
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_xpath_string.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_xpath_string.out
new file mode 100644
index 00000000000..3451f0737f4
Binary files /dev/null and 
b/regression-test/data/query_p0/sql_functions/string_functions/test_xpath_string.out
 differ
diff --git 
a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_date_arithmatic.groovy
 
b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_date_arithmatic.groovy
index bae40fd6796..a78067259dd 100644
--- 
a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_date_arithmatic.groovy
+++ 
b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_date_arithmatic.groovy
@@ -49,4 +49,33 @@ suite("fold_constant_date_arithmatic") {
     testFoldConst("select str_to_date('31/12/2020 11:59 PM', '%d/%m/%Y %h:%i 
%p');")
     testFoldConst("select str_to_date('20201231T235959', '%Y%m%dT%H%i%s');")
 
+    // test leap year and leap month edge cases
+    testFoldConst("select months_between('2020-02-29', '2020-02-29')")
+    testFoldConst("select months_between('2020-02-29', '2020-03-29')")
+    testFoldConst("select months_between('2020-01-29', '2020-02-29')")
+    testFoldConst("select months_between('2020-02-29', '2021-02-28')")
+    testFoldConst("select months_between('2019-02-28', '2020-02-29')")
+    
+    // test with time components in leap year
+    testFoldConst("select months_between('2020-02-29 12:00:00', '2020-02-29 
15:00:00')")
+    testFoldConst("select months_between('2020-02-29 23:59:59', '2020-03-29 
00:00:00')")
+    
+    // test with different round_off settings in leap year
+    testFoldConst("select months_between('2020-02-29', '2020-03-30', true)")
+    testFoldConst("select months_between('2020-02-29', '2020-03-30', false)")
+    
+    // test across multiple leap years
+    testFoldConst("select months_between('2020-02-29', '2024-02-29')")
+    testFoldConst("select months_between('2020-02-29 23:59:59', '2024-02-29 
00:00:00')")
+
+    // test case with last day of the month
+    testFoldConst("select months_between('2024-03-31', '2024-02-29')")
+    testFoldConst("select months_between('2024-03-30', '2024-02-29')")
+    testFoldConst("select months_between('2024-03-29', '2024-02-29')")
+
+    // Test next_day
+    testFoldConst("select next_day('2020-02-27', 'SAT');") // 2020-02-29 (leap 
year)
+    testFoldConst("select next_day('2020-02-29', 'MON');") // 2020-03-02 (leap 
year to next month)
+    testFoldConst("select next_day('2019-02-26', 'THU');") // 2019-02-28 
(non-leap year)
+    testFoldConst("select next_day('2019-02-28', 'SUN');") // 2019-03-03 
(non-leap year to next month)
 }
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
new file mode 100644
index 00000000000..aa760d97838
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
@@ -0,0 +1,147 @@
+// 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_months_between") {
+    sql "drop table if exists months_between_args;"
+    sql """
+        create table months_between_args (
+            k0 int,
+            date1_not_null datev2 not null,
+            date2_not_null datev2 not null,
+            datetime1_not_null datetimev2 not null,
+            datetime2_not_null datetimev2 not null,
+            round_off_not_null boolean not null,
+            date1_null datev2 null,
+            date2_null datev2 null,
+            datetime1_null datetimev2 null,
+            datetime2_null datetimev2 null,
+            round_off_null boolean null
+        )
+        DISTRIBUTED BY HASH(k0)
+        PROPERTIES
+        (
+            "replication_num" = "1"
+        );
+    """
+
+    order_qt_empty_nullable "select months_between(date1_null, date2_null), 
months_between(date1_null, date2_null, round_off_null), 
months_between(datetime1_null, datetime2_null, round_off_null), 
months_between(date1_null, datetime2_null, round_off_null), 
months_between(datetime1_null, date2_null, round_off_null) from 
months_between_args"
+    order_qt_empty_not_nullable "select months_between(date1_not_null, 
date2_not_null), months_between(date1_not_null, date2_not_null, 
round_off_not_null), months_between(datetime1_not_null, datetime2_not_null, 
round_off_not_null), months_between(date1_not_null, datetime2_not_null, 
round_off_not_null), months_between(datetime1_not_null, date2_not_null, 
round_off_not_null) from months_between_args"
+    order_qt_empty_partial_nullable "select months_between(date1_null, 
date2_not_null), months_between(date1_not_null, date2_null), 
months_between(date1_not_null, date2_not_null), months_between(datetime1_null, 
datetime2_not_null), months_between(datetime1_not_null, date2_null), 
months_between(datetime1_not_null, date2_not_null) from months_between_args"
+    order_qt_empty_nullable_no_null "select months_between(date1_null, 
nullable(date2_not_null)), months_between(nullable(date1_not_null), 
date2_null), months_between(date1_null, nullable(datetime2_not_null)), 
months_between(nullable(datetime1_not_null), date2_null), 
months_between(datetime1_null, nullable(date2_not_null)), 
months_between(nullable(datetime1_not_null), datetime2_null) from 
months_between_args"
+
+    sql "insert into months_between_args values (1, '2020-01-01', 
'2020-02-01', '2020-01-01 00:00:00', '2020-02-01 00:00:00', true, null, null, 
null, null, null)"
+
+    order_qt_nullable "select months_between(date1_null, date2_null), 
months_between(date1_null, date2_null, round_off_null), 
months_between(datetime1_null, datetime2_null, round_off_null), 
months_between(date1_null, datetime2_null, round_off_null), 
months_between(datetime1_null, date2_null, round_off_null) from 
months_between_args"
+    order_qt_not_nullable "select months_between(date1_not_null, 
date2_not_null), months_between(date1_not_null, date2_not_null, 
round_off_not_null), months_between(datetime1_not_null, datetime2_not_null, 
round_off_not_null), months_between(date1_not_null, datetime2_not_null, 
round_off_not_null), months_between(datetime1_not_null, date2_not_null, 
round_off_not_null) from months_between_args"
+    order_qt_partial_nullable "select months_between(date1_null, 
date2_not_null), months_between(date1_not_null, date2_null), 
months_between(date1_not_null, date2_not_null), months_between(datetime1_null, 
datetime2_not_null), months_between(datetime1_not_null, date2_null), 
months_between(datetime1_not_null, date2_not_null) from months_between_args"
+    order_qt_nullable_no_null "select months_between(date1_null, 
nullable(date2_not_null)), months_between(nullable(date1_not_null), 
date2_null), months_between(date1_null, nullable(datetime2_not_null)), 
months_between(nullable(datetime1_not_null), date2_null), 
months_between(datetime1_null, nullable(date2_not_null)), 
months_between(nullable(datetime1_not_null), datetime2_null) from 
months_between_args"
+
+    sql "truncate table months_between_args"
+
+    sql """
+    insert into months_between_args values 
+        (1, '2020-01-01', '2020-02-01', '2020-01-01 00:00:00', '2020-02-01 
00:00:00', true, null, null, null, null, null),
+        (2, '2020-01-01', '2020-02-01', '2020-01-01 00:00:00', '2020-02-01 
00:00:00', false, '2020-01-01', '2020-02-01', '2020-01-01 00:00:00', 
'2020-02-01 00:00:00', false),
+        (3, '2020-01-31', '2020-02-29', '2020-01-31 23:59:59', '2020-02-29 
23:59:59', true, '2020-01-31', '2020-02-29', '2020-01-31 23:59:59', '2020-02-29 
23:59:59', true),
+        (4, '2020-12-31', '2021-01-31', '2020-12-31 00:00:00', '2021-01-31 
00:00:00', false, null, '2021-01-31', null, '2021-01-31 00:00:00', false),
+        (5, '1900-01-01', '2100-12-31', '1900-01-01 00:00:00', '2100-12-31 
23:59:59', true, '1900-01-01', null, '1900-01-01 00:00:00', null, true),
+        (6, '2020-02-29', '2020-02-29', '2020-02-29 12:00:00', '2020-02-29 
12:00:00', false, '2020-02-29', '2020-02-29', null, '2020-02-29 12:00:00', 
null),
+        (7, '2020-01-15', '2020-02-15', '2020-01-15 15:30:00', '2020-02-15 
15:30:00', true, null, null, '2020-01-15 15:30:00', '2020-02-15 15:30:00', 
true),
+        (8, '2019-12-31', '2020-01-31', '2019-12-31 23:59:59', '2020-01-31 
00:00:00', false, '2019-12-31', null, null, null, null),
+        (9, '2020-06-30', '2020-07-01', '2020-06-30 23:59:59', '2020-07-01 
00:00:00', true, null, '2020-07-01', '2020-06-30 23:59:59', null, true),
+        (10, '2020-02-28', '2021-02-28', '2020-02-28 00:00:00', '2021-02-28 
00:00:00', false, '2020-02-28', '2021-02-28', null, '2021-02-28 00:00:00', 
false)
+    """
+
+    /// nullables
+    order_qt_nullable "select months_between(date1_null, date2_null), 
months_between(date1_null, date2_null, round_off_null), 
months_between(datetime1_null, datetime2_null, round_off_null), 
months_between(date1_null, datetime2_null, round_off_null), 
months_between(datetime1_null, date2_null, round_off_null) from 
months_between_args"
+    order_qt_not_nullable "select months_between(date1_not_null, 
date2_not_null), months_between(date1_not_null, date2_not_null, 
round_off_not_null), months_between(datetime1_not_null, datetime2_not_null, 
round_off_not_null), months_between(date1_not_null, datetime2_not_null, 
round_off_not_null), months_between(datetime1_not_null, date2_not_null, 
round_off_not_null) from months_between_args"
+    order_qt_partial_nullable "select months_between(date1_null, 
date2_not_null), months_between(date1_not_null, date2_null), 
months_between(date1_not_null, date2_not_null), months_between(datetime1_null, 
datetime2_not_null), months_between(datetime1_not_null, date2_null), 
months_between(datetime1_not_null, date2_not_null) from months_between_args"
+    order_qt_nullable_no_null "select months_between(date1_null, 
nullable(date2_not_null)), months_between(nullable(date1_not_null), 
date2_null), months_between(date1_null, nullable(datetime2_not_null)), 
months_between(nullable(datetime1_not_null), date2_null), 
months_between(datetime1_null, nullable(date2_not_null)), 
months_between(nullable(datetime1_not_null), datetime2_null) from 
months_between_args"
+
+    /// consts. most by BE-UT
+    order_qt_const_nullable "select months_between(NULL, NULL), 
months_between(NULL, NULL, NULL), months_between(NULL, NULL, NULL), 
months_between(NULL, NULL, NULL), months_between(NULL, NULL, NULL) from 
months_between_args"
+    order_qt_partial_const_nullable "select months_between(NULL, 
date2_not_null), months_between(date1_not_null, NULL), months_between(NULL, 
datetime2_not_null, round_off_not_null), months_between(datetime1_not_null, 
NULL, round_off_not_null) from months_between_args"
+    order_qt_const_not_nullable "select months_between('2020-01-01', 
'2020-02-01'), months_between('2020-01-01', '2020-02-01', true), 
months_between('2020-01-01 00:00:00', '2020-02-01 00:00:00', false), 
months_between('2020-01-01', '2020-02-01 00:00:00', true), 
months_between('2020-01-01 00:00:00', '2020-02-01', false) from 
months_between_args"
+    order_qt_const_other_nullable "select months_between('2020-01-01', 
date2_null), months_between(date1_null, '2020-02-01'), 
months_between('2020-01-01', datetime2_null, round_off_null), 
months_between(datetime1_null, '2020-02-01', round_off_null) from 
months_between_args"
+    order_qt_const_other_not_nullable "select months_between('2020-01-01', 
date2_not_null), months_between(date1_not_null, '2020-02-01'), 
months_between('2020-01-01', datetime2_not_null, round_off_not_null), 
months_between(datetime1_not_null, '2020-02-01', round_off_not_null) from 
months_between_args"
+    order_qt_const_nullable_no_null "select 
months_between(nullable('2020-01-01'), nullable('2020-02-01')), 
months_between(nullable('2020-01-01'), nullable('2020-02-01'), nullable(true)), 
months_between(nullable('2020-01-01 00:00:00'), nullable('2020-02-01 
00:00:00'), nullable(false)) from months_between_args"
+    order_qt_const_partial_nullable_no_null "select 
months_between('2020-01-01', nullable('2020-02-01')), 
months_between(nullable('2020-01-01'), '2020-02-01'), 
months_between('2020-01-01', nullable('2020-02-01 00:00:00'), nullable(true)) 
from months_between_args"
+    order_qt_const1 "select months_between('2020-01-01', date2_not_null), 
months_between('2020-01-01', date2_not_null, round_off_not_null) from 
months_between_args"
+    order_qt_const12 "select months_between('2020-01-01', '2020-02-01', 
round_off_not_null) from months_between_args"
+    order_qt_const23 "select months_between(date1_not_null, '2020-02-01', 
true) from months_between_args"
+    order_qt_const3 "select months_between(date1_not_null, date2_not_null, 
true) from months_between_args"
+
+    /// test simple cases with date and datetime combinations
+    order_qt_date_date "select months_between('2020-01-01', '2020-02-01')"
+    order_qt_date_datetime "select months_between('2020-01-01', '2020-02-01 
00:00:00')"
+    order_qt_datetime_date "select months_between(cast('2020-01-01 00:00:00' 
as datetimev2), cast('2020-02-01' as datev2))"
+    order_qt_datetime_datetime "select months_between(cast('2020-01-01 
00:00:00' as datetimev2), cast('2020-02-01 00:00:00' as datetimev2))"
+
+    order_qt_date_date_round "select months_between('2020-01-15', 
'2020-02-15', true)"
+    order_qt_date_datetime_round "select months_between('2020-01-15', 
'2020-02-15 12:00:00', true)" 
+    order_qt_datetime_date_round "select months_between(cast('2020-01-15 
12:00:00' as datetimev2), cast('2020-02-15' as datev2), true)"
+    order_qt_datetime_datetime_round "select months_between(cast('2020-01-15 
12:00:00' as datetimev2), cast('2020-02-15 12:00:00' as datetimev2), true)"
+
+    order_qt_date_date_no_round "select months_between('2020-01-31', 
'2020-02-29', false)"
+    order_qt_date_datetime_no_round "select months_between('2020-01-31', 
'2020-02-29 23:59:59', false)"
+    order_qt_datetime_date_no_round "select months_between(cast('2020-01-31 
23:59:59' as datetimev2), cast('2020-02-29' as datev2), false)" 
+    order_qt_datetime_datetime_no_round "select 
months_between(cast('2020-01-31 23:59:59' as datetimev2), cast('2020-02-29 
23:59:59' as datetimev2), false)"
+
+    // test leap year and leap month edge cases
+    order_qt_leap_year_same "select months_between('2020-02-29', '2020-02-29')"
+    order_qt_leap_year_next "select months_between('2020-02-29', '2020-03-29')"
+    order_qt_leap_year_prev "select months_between('2020-01-29', '2020-02-29')"
+    order_qt_leap_year_next_year "select months_between('2020-02-29', 
'2021-02-28')"
+    order_qt_leap_year_prev_year "select months_between('2019-02-28', 
'2020-02-29')"
+    
+    // test with time components in leap year
+    order_qt_leap_year_time "select months_between('2020-02-29 12:00:00', 
'2020-02-29 15:00:00')"
+    order_qt_leap_year_time_next "select months_between('2020-02-29 23:59:59', 
'2020-03-29 00:00:00')"
+    
+    // test with different round_off settings in leap year
+    order_qt_leap_year_round "select months_between('2020-02-29', 
'2020-03-30', true)"
+    order_qt_leap_year_no_round "select months_between('2020-02-29', 
'2020-03-30', false)"
+    
+    // test across multiple leap years
+    order_qt_multi_leap_years "select months_between('2020-02-29', 
'2024-02-29')"
+    order_qt_multi_leap_years_time "select months_between('2020-02-29 
23:59:59', '2024-02-29 00:00:00')"
+
+    // test case with last day of the month
+    order_qt_last_day_of_month1 "select months_between('2024-03-31', 
'2024-02-29')"
+    order_qt_last_day_of_month2 "select months_between('2024-03-30', 
'2024-02-29')"
+    order_qt_last_day_of_month3 "select months_between('2024-03-29', 
'2024-02-29')"
+
+    /// Fold constant
+    check_fold_consistency "months_between('2020-01-01', '2020-02-01')"
+    // Test boundary cases with fold constant
+    check_fold_consistency "months_between('0001-01-01', '0001-01-01')" // 
Same date at min year
+    check_fold_consistency "months_between('9999-12-31', '9999-12-31')" // 
Same date at max year
+    check_fold_consistency "months_between('0001-01-01', '9999-12-31')" // Min 
to max year
+    check_fold_consistency "months_between('9999-12-31', '0001-01-01')" // Max 
to min year
+    
+    // Test boundary cases with time components
+    check_fold_consistency "months_between('0001-01-01 00:00:00', '0001-01-01 
23:59:59')" // Same date different times at min year
+    check_fold_consistency "months_between('9999-12-31 00:00:00', '9999-12-31 
23:59:59')" // Same date different times at max year
+    
+    // Test boundary cases with round_off
+    check_fold_consistency "months_between('0001-01-01', '0001-02-01', true)" 
// Min year with rounding
+    check_fold_consistency "months_between('9999-12-31', '9999-11-30', false)" 
// Max year without rounding
+    
+    // Test boundary cases with leap years
+    check_fold_consistency "months_between('0004-02-29', '0004-03-01')" // 
First leap year
+    check_fold_consistency "months_between('9996-02-29', '9996-03-01')" // 
Last leap year
+}
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_next_day.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_next_day.groovy
new file mode 100644
index 00000000000..c4ba8937971
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_next_day.groovy
@@ -0,0 +1,129 @@
+// 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_next_day") {
+    sql "drop table if exists next_day_args;"
+    sql """
+        create table next_day_args (
+            k0 int,
+            a date not null,
+            b date null,
+            c datetime not null,
+            d datetime null,
+            e string not null,
+            f string null,
+        )
+        DISTRIBUTED BY HASH(k0)
+        PROPERTIES
+        (
+            "replication_num" = "1"
+        );
+    """
+
+    order_qt_empty_nullable "select next_day(a, e), next_day(c, e) from 
next_day_args"
+    order_qt_empty_not_nullable "select next_day(b, f), next_day(d, f) from 
next_day_args"
+    order_qt_empty_partial_nullable "select next_day(a, f), next_day(c, f) 
from next_day_args"
+
+    sql "insert into next_day_args values (1, '2025-01-01', null, '2025-01-01 
00:00:00', null, 'MONDAY', null), (2, '2025-01-02', '2025-01-02', '2025-01-02 
00:00:00', '2025-01-02 00:00:00', 'TUESDAY', 'TUESDAY')"
+    order_qt_all_null "select next_day(b, f), next_day(d, f) from 
next_day_args"
+
+    sql "truncate table next_day_args"
+
+    sql """ 
+        insert into next_day_args(k0, a, b, c, d, e, f) values 
+        -- normal date
+        (1, '2024-03-15', '2024-03-15', '2024-03-15 10:00:00', '2024-03-15 
10:00:00', 'MON', 'MONDAY'),
+        
+        -- first week of 0000
+        (2, '0000-01-01', '0000-01-01', '0000-01-01 00:00:00', '0000-01-01 
00:00:00', 'SUN', 'SUNDAY'),
+        
+        -- last day of 0000
+        (3, '0000-12-31', '0000-12-31', '0000-12-31 23:59:59', '0000-12-31 
23:59:59', 'FRI', 'FRIDAY'),
+        
+        -- 0000-02-28
+        (4, '0000-02-28', '0000-02-28', '0000-02-28 12:00:00', '0000-02-28 
12:00:00', 'MO', 'MONDAY'),
+        
+        -- leap year date before and after
+        (5, '2024-02-28', '2024-02-28', '2024-02-28 00:00:00', '2024-02-28 
00:00:00', 'WED', 'WEDNESDAY'),
+        (6, '2024-02-29', '2024-02-29', '2024-02-29 00:00:00', '2024-02-29 
00:00:00', 'THU', 'THURSDAY'),
+
+        -- non leap year date before and after
+        (7, '2023-02-28', '2023-02-28', '2023-02-28 00:00:00', '2023-02-28 
00:00:00', 'TUE', 'TUESDAY'),
+        (8, '2023-03-01', '2023-03-01', '2023-03-01 00:00:00', '2023-03-01 
00:00:00', 'WE', 'WEDNESDAY'),
+        
+        -- 1900 non leap year date before and after
+        (9, '1900-02-28', '1900-02-28', '1900-02-28 00:00:00', '1900-02-28 
00:00:00', 'WED', 'WEDNESDAY'),
+        (10, '1900-03-01', '1900-03-01', '1900-03-01 00:00:00', '1900-03-01 
00:00:00', 'THU', 'THURSDAY'),
+                
+        -- last second of 9999
+        (11, '9999-12-31', '9999-12-31', '9999-12-31 23:59:59', '9999-12-31 
23:59:59', 'FRI', 'FRIDAY'),
+        
+        -- first second of 1970
+        (12, '1969-12-31', '1969-12-31', '1969-12-31 23:59:59', '1969-12-31 
23:59:59', 'WED', 'WEDNESDAY'),
+        (13, '1970-01-01', '1970-01-01', '1970-01-01 00:00:00', '1970-01-01 
00:00:00', 'THU', 'THURSDAY');
+    """
+
+    order_qt_nullable "select next_day(b, f), next_day(d, f) from 
next_day_args"
+    order_qt_not_nullable "select next_day(a, e), next_day(c, e) from 
next_day_args"
+    order_qt_partial_nullable "select next_day(a, f), next_day(c, f), 
next_day(b, e), next_day(d, e) from next_day_args"
+    order_qt_nullable_no_null "select next_day(a, nullable(e)), next_day(c, 
nullable(e)) from next_day_args"
+
+    /// consts. most by BE-UT
+    order_qt_const_nullable "select next_day(NULL, NULL) from next_day_args"
+    order_qt_partial_const_nullable "select next_day(NULL, e) from 
next_day_args"
+    order_qt_const_not_nullable "select next_day('2025-01-01', 'MONDAY') from 
next_day_args"
+    order_qt_const_other_nullable "select next_day('2025-01-01', f) from 
next_day_args"
+    order_qt_const_other_not_nullable "select next_day(a, 'FRI') from 
next_day_args"
+    order_qt_const_nullable_no_null "select next_day(nullable('2025-01-01'), 
nullable('MON'))"
+    order_qt_const_nullable_no_null_multirows "select next_day(nullable(c), 
nullable(e)) from next_day_args"
+    order_qt_const_partial_nullable_no_null "select next_day('2025-01-01', 
nullable(e)) from next_day_args"
+
+    /// folding
+    check_fold_consistency "next_day('', 'SA')"
+    check_fold_consistency "next_day(NULL, NULL)"
+    check_fold_consistency "next_day(NULL, 'FRI')"
+    check_fold_consistency "next_day('2025-01-01', NULL)"
+    check_fold_consistency "next_day('2025-01-01', 'MONDAY')"
+    check_fold_consistency "next_day(nullable('2025-01-01'), nullable('SUN'))"
+    check_fold_consistency "next_day('2025-01-01', nullable('WE'))"
+    check_fold_consistency "next_day(nullable('2025-01-01'), 'TH')"
+    check_fold_consistency "next_day(nullable('2025-01-01 12:34:56'), 
'MONDAY')"  
+    // test cast date/datetime to date/datetime
+    check_fold_consistency "next_day(cast('2025-01-01' as date), 'MONDAY')"
+    check_fold_consistency "next_day(cast('2025-01-01' as datetime), 'MONDAY')"
+    check_fold_consistency "next_day(cast('2025-01-01 12:34:56' as date), 
'MONDAY')"
+    check_fold_consistency "next_day(cast('2025-01-01 12:34:56' as datetime), 
'MONDAY')"
+
+    /// wrong date
+    order_qt_wrong_date "select next_day('2025-02-31', 'MONDAY')"
+    order_qt_wrong_date "select next_day('2025-02-29', 'MONDAY')"
+    order_qt_wrong_date "select next_day('2025-03-32', 'MONDAY')"
+
+    /// error cases
+    test{
+        sql """ select next_day('2025-01-01', 'SO') """
+        exception "Function next_day failed to parse weekday: SO"
+    }
+    test{
+        sql """ select next_day('2025-01-01', 'MONDDY') """
+        exception "Function next_day failed to parse weekday: MONDDY"
+    }
+    test{
+        sql """ select next_day('2025-01-01', '') """
+        exception "Function next_day failed to parse weekday: "
+    }
+}
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_str_to_map.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_str_to_map.groovy
new file mode 100644
index 00000000000..e11e2310b73
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_str_to_map.groovy
@@ -0,0 +1,232 @@
+// 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_str_to_map") {
+    sql "drop table if exists str_to_map_args;"
+    sql """
+        create table str_to_map_args (
+            k0 int,
+            map_str_not_null string not null,
+            map_str_null string null,
+            key_delim_not_null string not null,
+            key_delim_null string null,
+            value_delim_not_null string not null,
+            value_delim_null string null
+        )
+        DISTRIBUTED BY HASH(k0)
+        PROPERTIES
+        (
+            "replication_num" = "1"
+        );
+    """
+
+    // Test empty table with different nullable combinations
+    order_qt_all_not_null "select str_to_map(map_str_not_null, 
key_delim_not_null, value_delim_not_null) from str_to_map_args"
+    
+    order_qt_all_args_null "select str_to_map(map_str_null, key_delim_null, 
value_delim_null) from str_to_map_args"
+    
+    order_qt_partial_null "select str_to_map(map_str_not_null, key_delim_null, 
value_delim_null) from str_to_map_args"
+    
+    order_qt_nullable_no_null "select str_to_map(nullable(map_str_not_null), 
nullable(key_delim_not_null), nullable(value_delim_not_null)) from 
str_to_map_args"
+
+    sql '''
+    insert into str_to_map_args values
+        (1, 'a:1,b:2,c:3', 'a:1,b:2,c:3', ',', ',', ':', ':'),
+        (2, '', '', ',', ',', ':', ':'), -- Empty string test
+        (3, 'a:1', 'a:1', ',', ',', ':', ':'), -- Single key-value pair
+        (4, 'a:1,b:2,b:3', 'a:1,b:2,b:3', ',', ',', ':', ':'), -- Duplicate 
keys
+        (5, 'a:,b:,c:', 'a:,b:,c:', ',', ',', ':', ':'), -- Empty values
+        (6, ':1,:2,:3', ':1,:2,:3', ',', ',', ':', ':'), -- Empty keys
+        (7, 'a=1;b=2;c=3', 'a=1;b=2;c=3', ';', ';', '=', '='), -- Different 
delimiters
+        (8, '中文:值,英文:value', '中文:值,英文:value', ',', ',', ':', ':'), -- Unicode 
characters
+        (9, 'special@#:123,chars!:456', 'special@#:123,chars!:456', ',', ',', 
':', ':'), -- Special characters in keys
+        (10, 'a:123!@#,b:456$%^', 'a:123!@#,b:456$%^', ',', ',', ':', ':'), -- 
Special characters in values
+        (11, 'verylongkey:verylongvalue,anotherlongkey:anotherlongvalue', 
'verylongkey:verylongvalue,anotherlongkey:anotherlongvalue', ',', ',', ':', 
':'), -- Long strings
+        (12, 'a::1,b::2', 'a::1,b::2', ',', ',', '::', '::'), -- 
Multi-character delimiter
+        (13, 'a:1\nb:2\nc:3', 'a:1\nb:2\nc:3', '\n', '\n', ':', ':'), -- 
Newline as delimiter
+        (14, 'a:1\tb:2\tc:3', 'a:1\tb:2\tc:3', '\t', '\t', ':', ':'), -- Tab 
as delimiter
+        (15, ' a : 1 , b : 2 ', ' a : 1 , b : 2 ', ',', ',', ':', ':') -- 
Spaces in string
+    '''
+
+    // Test different nullable combinations with data
+    order_qt_all_not_null_data """
+        select str_to_map(map_str_not_null, key_delim_not_null, 
value_delim_not_null) 
+        from str_to_map_args 
+        order by k0;
+    """
+
+    order_qt_all_args_null_data """
+        select str_to_map(map_str_null, key_delim_null, value_delim_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    order_qt_partial_null_data """
+        select str_to_map(map_str_not_null, key_delim_null, value_delim_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    order_qt_nullable_no_null_data """
+        select str_to_map(nullable(map_str_not_null), 
nullable(key_delim_not_null), nullable(value_delim_not_null))
+        from str_to_map_args
+        order by k0;
+    """
+
+    // Test mixed nullable combinations
+    order_qt_mixed_null_1 """
+        select str_to_map(map_str_null, key_delim_not_null, 
value_delim_not_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    order_qt_mixed_null_2 """
+        select str_to_map(map_str_not_null, key_delim_null, 
value_delim_not_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    order_qt_mixed_null_3 """
+        select str_to_map(map_str_not_null, key_delim_not_null, 
value_delim_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    // Test with constant null values
+    order_qt_const_null_1 """
+        select str_to_map(null, key_delim_not_null, value_delim_not_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    order_qt_const_null_2 """
+        select str_to_map(map_str_not_null, null, value_delim_not_null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    order_qt_const_null_3 """
+        select str_to_map(map_str_not_null, key_delim_not_null, null)
+        from str_to_map_args
+        order by k0;
+    """
+
+    /// consts. most by BE-UT
+    // Test const string with column delimiters
+    order_qt_const_str """
+        select str_to_map('a:1,b:2', key_delim_not_null, value_delim_not_null) 
+        from str_to_map_args order by k0
+    """
+    
+    // Test column string with const delimiters  
+    order_qt_const_delims """
+        select str_to_map(map_str_not_null, ',', ':') 
+        from str_to_map_args order by k0
+    """
+        
+    // Test const string with one const delimiter and one column delimiter
+    order_qt_mixed_const1 """
+        select str_to_map('x=1;y=2', ';', value_delim_not_null) 
+        from str_to_map_args order by k0
+    """
+    
+    order_qt_mixed_const2 """
+        select str_to_map('p-1|q-2', key_delim_not_null, '-') 
+        from str_to_map_args order by k0
+    """
+    
+    // Test all const non-null arguments
+    order_qt_all_const """
+        select str_to_map('a=1|b=2', '|', '=') 
+        from str_to_map_args order by k0
+    """
+    
+    // Test const string with nullable column delimiters
+    order_qt_const_str_null_delims """
+        select str_to_map('m:1,n:2', key_delim_null, value_delim_null) 
+        from str_to_map_args order by k0
+    """
+    
+    // Test nullable column string with const delimiters
+    order_qt_null_str_const_delims '''
+        select str_to_map(map_str_null, '#', '$') 
+        from str_to_map_args order by k0
+    '''
+
+    // Test basic str_to_map functionality with all parameters
+    qt_basic_1 "select str_to_map('a:1,b:2,c:3', ',', ':');"
+    qt_basic_2 "select str_to_map('key1=val1;key2=val2', ';', '=');"
+    qt_basic_3 "select str_to_map('x-1|y-2|z-3', '|', '-');"
+
+    // Test with default parameters (omitting both delimiters)
+    // Default pair delimiter is ',' and key-value delimiter is ':'
+    qt_default_both_1 "select str_to_map('a:1,b:2,c:3');"
+    qt_default_both_2 "select str_to_map('key1:value1,key2:value2');"
+    qt_default_both_3 "select str_to_map('x:1,y:2,z:');"
+    qt_default_both_4 "select str_to_map('');"
+
+    // Test with default key-value delimiter (omitting last parameter)
+    // Default key-value delimiter is ':'
+    qt_default_value_1 "select str_to_map('a:1;b:2;c:3', ';');"
+    qt_default_value_2 "select str_to_map('key:val|foo:bar', '|');"
+    qt_default_value_3 "select str_to_map('x:1#y:2#z:3', '#');"
+    qt_default_value_4 "select str_to_map('a:1...b:2...c:3', '...');"
+
+    // Test empty string cases
+    qt_empty_1 "select str_to_map('');"
+    qt_empty_2 "select str_to_map('a:1,,b:2');" 
+    qt_empty_3 "select str_to_map('a:,b:2,c:');"
+    qt_empty_4 "select str_to_map(',,,');"
+
+    // Test missing key-value delimiter
+    qt_missing_value_1 "select str_to_map('a,b:2,c');"
+    qt_missing_value_2 "select str_to_map('val1,val2,val3');"
+    qt_missing_value_3 "select str_to_map('key1,key2:val2,key3');"
+
+    // Test with special characters
+    qt_special_1 "select str_to_map('\ta:1\n,\tb:2\n');"
+    qt_special_2 "select str_to_map('a\\nb:1,c\\td:2');"
+    qt_special_3 "select str_to_map('key1:value1,key2:value2', ',', ':');"
+
+    // Test with spaces
+    qt_spaces_1 "select str_to_map('a : 1, b : 2');"
+    qt_spaces_2 "select str_to_map(' a:1 , b:2 ');"
+    qt_spaces_3 "select str_to_map('   a:1,   b:2   ');"
+    qt_spaces_4 "select str_to_map(' ');"
+
+    // Test with Unicode characters
+    qt_unicode_1 "select str_to_map('键1:值1,键2:值2');"
+    qt_unicode_2 "select str_to_map('标题①:内容①,标题②:内容②');"
+    qt_unicode_3 "select str_to_map('🔑:🔒,📝:📖');"
+    qt_unicode_4 "select str_to_map('あ:い,う:え');"
+
+    // Test with duplicate keys
+    qt_dup_1 "select str_to_map('a:1,b:2,a:3');"
+    qt_dup_2 "select str_to_map('key:val1,key:val2,key:val3');"
+    qt_dup_3 "select str_to_map('a:1,a:,a:3');"
+
+    // Test edge cases
+    qt_edge_1 "select str_to_map('a:1:2,b:3:4');"
+    qt_edge_2 "select str_to_map(':::');"
+    qt_edge_3 "select str_to_map('a:1:2');"
+    qt_edge_4 "select str_to_map('key::value');"
+    qt_edge_5 "select str_to_map(':');"
+
+    // Test extremely long strings
+    qt_long_1 "select str_to_map(repeat('a:1,', 1000));"
+    qt_long_2 "select str_to_map(concat(repeat('key', 100), ':', 
repeat('value', 100)));"
+}
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_xpath_string.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_xpath_string.groovy
new file mode 100644
index 00000000000..c73f70b26f2
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_xpath_string.groovy
@@ -0,0 +1,149 @@
+// 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_xpath_string") {
+    sql "drop table if exists xpath_string_args;"
+    sql """
+        create table xpath_string_args (
+            k0 int,
+            xml_not_null string not null,
+            xml_null string null,
+            xpath_not_null string not null,
+            xpath_null string null
+        )
+        DISTRIBUTED BY HASH(k0)
+        PROPERTIES
+        (
+            "replication_num" = "1"
+        );
+    """
+
+    order_qt_empty_nullable "select xpath_string(xml_null, xpath_null) from 
xpath_string_args"
+    order_qt_empty_not_nullable "select xpath_string(xml_not_null, 
xpath_not_null) from xpath_string_args"
+    order_qt_empty_partial_nullable "select xpath_string(xml_null, 
xpath_not_null), xpath_string(xml_not_null, xpath_null) from xpath_string_args"
+
+    sql "insert into xpath_string_args values (1, '<a><b>123</b></a>', null, 
'/a/b', null)"
+
+    order_qt_all_null "select xpath_string(xml_null, xpath_null) from 
xpath_string_args"
+    order_qt_all_not_null "select xpath_string(xml_not_null, xpath_not_null) 
from xpath_string_args"
+    order_qt_partial_nullable "select xpath_string(xml_null, xpath_not_null), 
xpath_string(xml_not_null, xpath_null) from xpath_string_args"
+    order_qt_nullable_no_null "select xpath_string(xml_null, 
nullable(xpath_not_null)), xpath_string(nullable(xml_not_null), xpath_null) 
from xpath_string_args"
+
+    sql "truncate table xpath_string_args"
+
+    sql """
+    insert into xpath_string_args values 
+        (2, '<a>123</a>', '<a>456</a>', '/a', '/a'),
+        (3, '<a><b>123</b><c>456</c></a>', null, '/a/c', '/a/b'),
+        (4, '<a><b>123</b><c>456</c></a>', '<a><d>789</d></a>', '/a/d', null),
+        (5, '<a><b>123</b><b>456</b></a>', '<a><b>789</b></a>', '/a/b[1]', 
'/a/b'),
+        (6, '<a><b>123</b><b>456</b></a>', null, '/a/b[2]', '/a/b[1]'),
+        (7, '<a><b attr="val">123</b></a>', '<a><b attr="other">456</b></a>', 
'/a/b[@attr]', '/a/b[@attr="val"]'),
+        (8, '<a><!-- comment -->123</a>', '<a>456</a>', '/a', null),
+        (9, '<a><![CDATA[123]]></a>', null, '/a', '/a'),
+        (10, '<a>123<b>456</b>789</a>', '<a><b>test</b></a>', '/a', '/a/b'),
+        (11, '<a>  123  </a>', '<a>456</a>', '/a', null),
+        (12, '<book><title>Intro to Hive</title><author>John 
Doe</author></book>', 
+            '<book><title>SQL Guide</title></book>', 
+            '//title/text()', 
+            '//author/text()'),
+        (13, '<root><user 
id="1"><name>Alice</name><age>25</age></user></root>',
+            '<root><user id="2"><name>Bob</name></user></root>',
+            '/root/user[@id="1"]/name',
+            '/root/user/age'),
+        (14, '<products><item price="10.99">Book</item><item 
price="20.99">Pen</item></products>',
+            null,
+            '/products/item[@price="20.99"]',
+            '/products/item[1]'),
+        (15, '<data><![CDATA[<nested>value</nested>]]></data>',
+            '<data><plain>text</plain></data>',
+            '/data',
+            '//plain/text()'),
+        (16, 
'<menu><item>Coffee<price>3.99</price></item><item>Tea<price>2.99</price></item></menu>',
+            '<menu><item><price>5.99</price></item></menu>',
+            '//item[price="2.99"]',
+            '/menu/item[1]/price'),
+        (17, '<doc><section id="1">First</section><section 
id="2">Second</section></doc>',
+            null,
+            '/doc/section[@id="2"]',
+            '/doc/section[1]'),
+        (18, '<list><elem pos="1">A</elem><elem pos="2">B</elem><elem 
pos="3">C</elem></list>',
+            '<list><elem>X</elem></list>',
+            '/list/elem[@pos="2"]',
+            '/list/elem[last()]'),
+        (19, '<nested><a><b><c>Deep</c></b></a></nested>',
+            '<nested><x><y>Shallow</y></x></nested>',
+            '//c',
+            '/nested/x/y'),
+        (20, '<mixed>Text<b>Bold</b>Normal<i>Italic</i>End</mixed>',
+            '<mixed><b>Only Bold</b></mixed>',
+            '/mixed',
+            '//b/text()'),
+        (21, '<empty></empty>',
+            '<empty/>',
+            '/empty',
+            '/empty/text()')
+    """
+
+    order_qt_all_null "select xpath_string(xml_null, xpath_null) from 
xpath_string_args"
+    order_qt_all_not_null "select xpath_string(xml_not_null, xpath_not_null) 
from xpath_string_args"
+    order_qt_partial_nullable "select xpath_string(xml_null, xpath_not_null), 
xpath_string(xml_not_null, xpath_null) from xpath_string_args"
+    order_qt_nullable_no_null "select xpath_string(xml_null, 
nullable(xpath_not_null)), xpath_string(nullable(xml_not_null), xpath_null) 
from xpath_string_args"
+
+    /// consts. most by BE-UT
+    order_qt_const_nullable "select xpath_string(xml_null, NULL), 
xpath_string(NULL, xpath_null) from xpath_string_args"
+    order_qt_const_not_nullable "select xpath_string(xml_not_null, '/a/b'), 
xpath_string('<a><b>123</b></a>', xpath_not_null) from xpath_string_args"
+    order_qt_const_partial_nullable "select xpath_string(xml_null, 
nullable('/a/b')), xpath_string(xml_not_null, nullable(xpath_null)) from 
xpath_string_args"
+    order_qt_const_nullable_no_null "select 
xpath_string(nullable(xml_not_null), nullable('/a/b')), 
xpath_string(nullable('<a><b>123</b></a>'), nullable(xpath_not_null)) from 
xpath_string_args"
+
+    order_qt_1 "select xpath_string('', '')"
+    order_qt_2 "select xpath_string(NULL, NULL)"
+    order_qt_3 "select xpath_string(NULL, '/a/b')"
+    order_qt_4 "select xpath_string('<a><b>123</b></a>', NULL)"
+    order_qt_5 "select xpath_string('<a><b>123</b></a>', '/a/b')"
+    order_qt_6 "select xpath_string('<a>123</a>', '/a')"
+    order_qt_7 "select xpath_string('<a><b>123</b><c>456</c></a>', '/a/c')"
+    order_qt_8 "select xpath_string('<a><b>123</b><b>456</b></a>', '/a/b[1]')"
+    order_qt_9 "select xpath_string('<a><b attr=\"val\">123</b></a>', 
'/a/b[@attr]')"
+    order_qt_10 "select xpath_string('<a><!-- comment -->123</a>', '/a')"
+    order_qt_11 "select xpath_string('<a><![CDATA[123]]></a>', '/a')"
+    order_qt_12 "select xpath_string('<book><title>Intro to 
Hive</title></book>', '//title/text()')"
+    order_qt_13 "select xpath_string(nullable('<a><b>123</b></a>'), 
nullable('/a/b'))"
+    order_qt_14 "select xpath_string('<a><b>123</b></a>', nullable('/a/b'))"
+    order_qt_15 "select xpath_string(nullable('<a><b>123</b></a>'), '/a/b')"
+    order_qt_16 "select xpath_string('<root><user 
id=\"1\"><name>Alice</name></user></root>', '/root/user[@id=\"1\"]/name')"
+    order_qt_17 "select xpath_string('<products><item 
price=\"10.99\">Book</item></products>', '/products/item[@price=\"10.99\"]')"
+    order_qt_18 "select 
xpath_string('<menu><item><price>3.99</price></item></menu>', 
'//item/price/text()')"
+    order_qt_19 "select xpath_string('<data><a>1</a><a>2</a><a>3</a></data>', 
'/data/a[last()]')"
+    order_qt_20 "select 
xpath_string('<nested><a><b><c>Deep</c></b></a></nested>', '//c/text()')"
+    order_qt_21 "select xpath_string('<mixed>Text<b>Bold</b>Normal</mixed>', 
'/mixed/text()')"
+    order_qt_22 "select xpath_string('<doc><item 
pos=\"1\">First</item></doc>', '/doc/item[@pos=\"1\"]/text()')"
+    order_qt_23 "select xpath_string('<test><a>x</a><b>y</b><c>z</c></test>', 
'/test/*[2]')"
+    order_qt_24 "select 
xpath_string('<data><![CDATA[<nested>value</nested>]]></data>', '/data')"
+    order_qt_25 "select xpath_string('<root><elem><!-- comment 
-->value</elem></root>', '/root/elem')"
+    order_qt_26 "select 
xpath_string('<doc><section><title>Test</title><para>Text</para></section></doc>',
 '/doc/section[title=\"Test\"]/para')"
+    order_qt_27 "select xpath_string('<list><item val=\"1\"/><item 
val=\"2\"/></list>', '/list/item[@val=\"2\"]')"
+    order_qt_28 "select 
xpath_string('<data><group><name>A</name><value>1</value></group></data>', 
'/data/group[name=\"A\"]/value')"
+    order_qt_29 "select 
xpath_string('<root><a><b>1</b></a><a><b>2</b></a></root>', '//a[b=\"2\"]/b')"
+    order_qt_30 "select xpath_string('<doc><p 
class=\"main\">Content</p></doc>', '//p[@class=\"main\"]/text()')"
+
+    /// error cases:
+    test {
+        sql """ select xpath_string('wrong xml', '//a/c') """
+        exception "Function xpath_string failed to parse XML string: No 
document element found"
+    }
+}


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

Reply via email to