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

commit 6248c68f3cf6141cee98ad69f3f90842573c2bcc
Author: yangshijie <sjyang2...@zju.edu.cn>
AuthorDate: Mon Jul 1 11:34:35 2024 +0800

    [feature](function) support ip functions named ipv4_to_ipv6 and cut_ipv6 
(#36883)
    
    - ipv4_to_ipv6: accept an IPv4 type address and return the converted
    IPv6 type address.
    - cut_ipv6: accept an IPv6 type address and return a string containing
    the address of the specified number of bytes removed in text format.
    - improve ut framework to support parsing of IPv4 and IPv6 types.
    - add some uts and regression tests for these two functions.
---
 be/src/vec/functions/function_ip.cpp               |   6 +
 be/src/vec/functions/function_ip.h                 | 137 +++++++++++++++++++++
 be/test/vec/function/function_ip_test.cpp          |  75 +++++++++++
 be/test/vec/function/function_test_util.cpp        |  16 +++
 be/test/vec/function/function_test_util.h          |   3 +
 .../doris/catalog/BuiltinScalarFunctions.java      |  16 ++-
 .../expressions/functions/scalar/CutIpv6.java      |  67 ++++++++++
 .../expressions/functions/scalar/Ipv4ToIpv6.java   |  65 ++++++++++
 .../expressions/visitor/ScalarFunctionVisitor.java |  58 +++++----
 gensrc/script/doris_builtins_functions.py          |   2 +
 .../ip_functions/test_cut_ipv6_function.out        |  19 +++
 .../ip_functions/test_ipv4_to_ipv6_function.out    |  14 +++
 .../ip_functions/test_cut_ipv6_function.groovy     |  56 +++++++++
 .../ip_functions/test_ipv4_to_ipv6_function.groovy |  51 ++++++++
 14 files changed, 555 insertions(+), 30 deletions(-)

diff --git a/be/src/vec/functions/function_ip.cpp 
b/be/src/vec/functions/function_ip.cpp
index dbb715ee7f6..ae5a2399981 100644
--- a/be/src/vec/functions/function_ip.cpp
+++ b/be/src/vec/functions/function_ip.cpp
@@ -58,5 +58,11 @@ void register_function_ip(SimpleFunctionFactory& factory) {
     factory.register_function<FunctionToIP<IPConvertExceptionMode::Throw, 
IPv6>>();
     factory.register_function<FunctionToIP<IPConvertExceptionMode::Default, 
IPv6>>();
     factory.register_function<FunctionToIP<IPConvertExceptionMode::Null, 
IPv6>>();
+
+    /// Convert between IPv4 and IPv6 part
+    factory.register_function<FunctionIPv4ToIPv6>();
+
+    /// Cut IPv6 part
+    factory.register_function<FunctionCutIPv6>();
 }
 } // namespace doris::vectorized
\ No newline at end of file
diff --git a/be/src/vec/functions/function_ip.h 
b/be/src/vec/functions/function_ip.h
index 69c7a20e896..3b02d779246 100644
--- a/be/src/vec/functions/function_ip.h
+++ b/be/src/vec/functions/function_ip.h
@@ -25,13 +25,16 @@
 #include <memory>
 
 #include "vec/columns/column.h"
+#include "vec/columns/column_const.h"
 #include "vec/columns/column_nullable.h"
 #include "vec/columns/column_string.h"
 #include "vec/columns/column_struct.h"
 #include "vec/columns/column_vector.h"
 #include "vec/columns/columns_number.h"
+#include "vec/common/assert_cast.h"
 #include "vec/common/format_ip.h"
 #include "vec/common/ipv6_to_binary.h"
+#include "vec/common/unaligned.h"
 #include "vec/core/column_with_type_and_name.h"
 #include "vec/core/types.h"
 #include "vec/data_types/data_type.h"
@@ -1159,4 +1162,138 @@ public:
     }
 };
 
+class FunctionIPv4ToIPv6 : public IFunction {
+public:
+    static constexpr auto name = "ipv4_to_ipv6";
+    static FunctionPtr create() { return 
std::make_shared<FunctionIPv4ToIPv6>(); }
+
+    String get_name() const override { return name; }
+
+    size_t get_number_of_arguments() const override { return 1; }
+
+    DataTypePtr get_return_type_impl(const DataTypes& arguments) const 
override {
+        return std::make_shared<DataTypeIPv6>();
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        const auto& ipv4_column_with_type_and_name = 
block.get_by_position(arguments[0]);
+        const auto& [ipv4_column, ipv4_const] =
+                unpack_if_const(ipv4_column_with_type_and_name.column);
+        const auto* ipv4_addr_column = assert_cast<const 
ColumnIPv4*>(ipv4_column.get());
+        const auto& ipv4_column_data = ipv4_addr_column->get_data();
+        auto col_res = ColumnIPv6::create(input_rows_count, 0);
+        auto& col_res_data = col_res->get_data();
+
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            auto ipv4_idx = index_check_const(i, ipv4_const);
+            map_ipv4_to_ipv6(ipv4_column_data[ipv4_idx],
+                             reinterpret_cast<UInt8*>(&col_res_data[i]));
+        }
+
+        block.replace_by_position(result, std::move(col_res));
+        return Status::OK();
+    }
+
+private:
+    static void map_ipv4_to_ipv6(IPv4 ipv4, UInt8* buf) {
+        unaligned_store<UInt64>(buf, 0x0000FFFF00000000ULL | 
static_cast<UInt64>(ipv4));
+        unaligned_store<UInt64>(buf + 8, 0);
+    }
+};
+
+class FunctionCutIPv6 : public IFunction {
+public:
+    static constexpr auto name = "cut_ipv6";
+    static FunctionPtr create() { return std::make_shared<FunctionCutIPv6>(); }
+
+    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<DataTypeString>();
+    }
+
+    Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
+                        size_t result, size_t input_rows_count) const override 
{
+        const auto& ipv6_column_with_type_and_name = 
block.get_by_position(arguments[0]);
+        const auto& bytes_to_cut_for_ipv6_column_with_type_and_name =
+                block.get_by_position(arguments[1]);
+        const auto& bytes_to_cut_for_ipv4_column_with_type_and_name =
+                block.get_by_position(arguments[2]);
+
+        const auto& [ipv6_column, ipv6_const] =
+                unpack_if_const(ipv6_column_with_type_and_name.column);
+        const auto& [bytes_to_cut_for_ipv6_column, 
bytes_to_cut_for_ipv6_const] =
+                
unpack_if_const(bytes_to_cut_for_ipv6_column_with_type_and_name.column);
+        const auto& [bytes_to_cut_for_ipv4_column, 
bytes_to_cut_for_ipv4_const] =
+                
unpack_if_const(bytes_to_cut_for_ipv4_column_with_type_and_name.column);
+
+        const auto* ipv6_addr_column = assert_cast<const 
ColumnIPv6*>(ipv6_column.get());
+        const auto* to_cut_for_ipv6_bytes_column =
+                assert_cast<const 
ColumnInt8*>(bytes_to_cut_for_ipv6_column.get());
+        const auto* to_cut_for_ipv4_bytes_column =
+                assert_cast<const 
ColumnInt8*>(bytes_to_cut_for_ipv4_column.get());
+
+        const auto& ipv6_addr_column_data = ipv6_addr_column->get_data();
+        const auto& to_cut_for_ipv6_bytes_column_data = 
to_cut_for_ipv6_bytes_column->get_data();
+        const auto& to_cut_for_ipv4_bytes_column_data = 
to_cut_for_ipv4_bytes_column->get_data();
+
+        auto col_res = ColumnString::create();
+        ColumnString::Chars& chars_res = col_res->get_chars();
+        ColumnString::Offsets& offsets_res = col_res->get_offsets();
+        chars_res.resize(input_rows_count * (IPV6_MAX_TEXT_LENGTH + 1)); // + 
1 for ending '\0'
+        offsets_res.resize(input_rows_count);
+        auto* begin = reinterpret_cast<char*>(chars_res.data());
+        auto* pos = begin;
+
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            auto ipv6_idx = index_check_const(i, ipv6_const);
+            auto bytes_to_cut_for_ipv6_idx = index_check_const(i, 
bytes_to_cut_for_ipv6_const);
+            auto bytes_to_cut_for_ipv4_idx = index_check_const(i, 
bytes_to_cut_for_ipv4_const);
+
+            auto* address = const_cast<unsigned char*>(
+                    reinterpret_cast<const unsigned 
char*>(&ipv6_addr_column_data[ipv6_idx]));
+            Int8 bytes_to_cut_for_ipv6_count =
+                    
to_cut_for_ipv6_bytes_column_data[bytes_to_cut_for_ipv6_idx];
+            Int8 bytes_to_cut_for_ipv4_count =
+                    
to_cut_for_ipv4_bytes_column_data[bytes_to_cut_for_ipv4_idx];
+
+            if (bytes_to_cut_for_ipv6_count > IPV6_BINARY_LENGTH) [[unlikely]] 
{
+                throw Exception(ErrorCode::INVALID_ARGUMENT,
+                                "Illegal value for argument 2 {} of function 
{}",
+                                
bytes_to_cut_for_ipv6_column_with_type_and_name.type->get_name(),
+                                get_name());
+            }
+
+            if (bytes_to_cut_for_ipv4_count > IPV6_BINARY_LENGTH) [[unlikely]] 
{
+                throw Exception(ErrorCode::INVALID_ARGUMENT,
+                                "Illegal value for argument 3 {} of function 
{}",
+                                
bytes_to_cut_for_ipv4_column_with_type_and_name.type->get_name(),
+                                get_name());
+            }
+
+            UInt8 bytes_to_cut_count = is_ipv4_mapped(address) ? 
bytes_to_cut_for_ipv4_count
+                                                               : 
bytes_to_cut_for_ipv6_count;
+            cut_address(address, pos, bytes_to_cut_count);
+            offsets_res[i] = pos - begin;
+        }
+
+        block.replace_by_position(result, std::move(col_res));
+        return Status::OK();
+    }
+
+private:
+    static bool is_ipv4_mapped(const UInt8* address) {
+        return (unaligned_load_little_endian<UInt64>(address + 8) == 0) &&
+               ((unaligned_load_little_endian<UInt64>(address) & 
0xFFFFFFFF00000000ULL) ==
+                0x0000FFFF00000000ULL);
+    }
+
+    static void cut_address(unsigned char* address, char*& dst, UInt8 
zeroed_tail_bytes_count) {
+        format_ipv6(address, dst, zeroed_tail_bytes_count);
+    }
+};
+
 } // namespace doris::vectorized
\ No newline at end of file
diff --git a/be/test/vec/function/function_ip_test.cpp 
b/be/test/vec/function/function_ip_test.cpp
index 6a16798b9bd..192b3810a98 100644
--- a/be/test/vec/function/function_ip_test.cpp
+++ b/be/test/vec/function/function_ip_test.cpp
@@ -18,6 +18,7 @@
 #include "function_test_util.h"
 #include "gtest/gtest_pred_impl.h"
 #include "vec/core/types.h"
+#include "vec/data_types/data_type_ipv6.h"
 #include "vec/data_types/data_type_number.h"
 
 namespace doris::vectorized {
@@ -80,4 +81,78 @@ TEST(FunctionIpTest, FunctionIsIPAddressInRangeTest) {
     }
 }
 
+TEST(FunctionIpTest, FunctionIPv4ToIPv6Test) {
+    std::string func_name = "ipv4_to_ipv6";
+
+    DataSet data_set = {
+            {{static_cast<IPv4>(0)}, static_cast<IPv6>(0xFFFF00000000ULL)},    
      // 0.0.0.0
+            {{static_cast<IPv4>(1)}, static_cast<IPv6>(0xFFFF00000001ULL)},    
      // 0.0.0.1
+            {{static_cast<IPv4>(2130706433)}, 
static_cast<IPv6>(0xFFFF7F000001ULL)}, // 127.0.0.1
+            {{static_cast<IPv4>(3232235521)}, 
static_cast<IPv6>(0xFFFFC0A80001ULL)}, // 192.168.0.1
+            {{static_cast<IPv4>(4294967294)},
+             static_cast<IPv6>(0xFFFFFFFFFFFEULL)}, // 255.255.255.254
+            {{static_cast<IPv4>(4294967295)},
+             static_cast<IPv6>(0xFFFFFFFFFFFFULL)} // 255.255.255.255
+    };
+
+    InputTypeSet input_types = {TypeIndex::IPv4};
+    static_cast<void>(check_function<DataTypeIPv6, true>(func_name, 
input_types, data_set));
+}
+
+TEST(FunctionIpTest, FunctionCutIPv6Test) {
+    std::string func_name = "cut_ipv6";
+
+    std::array<std::array<uint8_t, 16>, 9> ipv6s = {
+            std::array<uint8_t, 16> {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0},   // ::
+            std::array<uint8_t, 16> {0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0}, // ::1
+            std::array<uint8_t, 16> {0x02, 0, 0x02, 0xb1, 0, 0, 0, 0, 0x10, 
0x06, 0xa1, 0, 0x70,
+                                     0x1b, 0x01, 0x20}, // 
2001:1b70:a1:610::b102:2
+            std::array<uint8_t, 16> {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff,
+                                     0xff, 0xff, 0xff, 0xff, 0xff,
+                                     0xff}, // 
ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe
+            std::array<uint8_t, 16> {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
0xff, 0xff, 0xff,
+                                     0xff, 0xff, 0xff, 0xff, 0xff,
+                                     0xff}, // 
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
+            std::array<uint8_t, 16> {0x01, 0, 0xa8, 0xc0, 0xff, 0xff, 0, 0, 0, 
0, 0, 0, 0, 0, 0,
+                                     0}, // ::ffff:192.168.0.1
+            std::array<uint8_t, 16> {0x01, 0, 0, 0x7f, 0xff, 0xff, 0, 0, 0, 0, 
0, 0, 0, 0, 0,
+                                     0}, // ::ffff:127.0.0.1
+            std::array<uint8_t, 16> {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 
0, 0, 0, 0, 0, 0, 0,
+                                     0}, // ::ffff:255.255.255.254
+            std::array<uint8_t, 16> {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 
0, 0, 0, 0, 0, 0, 0,
+                                     0} // ::ffff:255.255.255.255
+    };
+
+    std::vector<int8_t> bytes = {0, 2, 4, 8, 16, 2, 4, 8, 16};
+
+    std::vector<std::string> results = {"::",
+                                        "::",
+                                        "2001:1b70:a1:610::",
+                                        "ffff:ffff:ffff:ffff::",
+                                        "::",
+                                        "::ffff:192.168.0.0",
+                                        "::ffff:0.0.0.0",
+                                        "::",
+                                        "::"};
+
+    DataSet data_set;
+
+    for (int i = 0; i < 5; ++i) {
+        IPv6 ipv6;
+        std::memcpy(&ipv6, &ipv6s[i], sizeof(IPv6));
+        // *reinterpret_cast<uint128_t*> will result in core dump, using 
std::memcpy instead.
+        data_set.push_back({{ipv6, bytes[i], (int8_t)0}, results[i]});
+    }
+
+    for (int i = 5; i < results.size(); ++i) {
+        IPv6 ipv6;
+        std::memcpy(&ipv6, &ipv6s[i], sizeof(IPv6));
+        // *reinterpret_cast<uint128_t*> will result in core dump, using 
std::memcpy instead.
+        data_set.push_back({{ipv6, (int8_t)0, bytes[i]}, results[i]});
+    }
+
+    InputTypeSet input_types = {TypeIndex::IPv6, TypeIndex::Int8, 
TypeIndex::Int8};
+    static_cast<void>(check_function<DataTypeString, true>(func_name, 
input_types, data_set));
+}
+
 } // namespace doris::vectorized
diff --git a/be/test/vec/function/function_test_util.cpp 
b/be/test/vec/function/function_test_util.cpp
index 0025945ef19..88014c72735 100644
--- a/be/test/vec/function/function_test_util.cpp
+++ b/be/test/vec/function/function_test_util.cpp
@@ -30,6 +30,8 @@
 #include "vec/data_types/data_type_date.h"
 #include "vec/data_types/data_type_date_time.h"
 #include "vec/data_types/data_type_decimal.h"
+#include "vec/data_types/data_type_ipv4.h"
+#include "vec/data_types/data_type_ipv6.h"
 #include "vec/data_types/data_type_jsonb.h"
 #include "vec/data_types/data_type_string.h"
 #include "vec/data_types/data_type_time_v2.h"
@@ -98,6 +100,14 @@ size_t type_index_to_data_type(const std::vector<AnyType>& 
input_types, size_t i
         desc.type = doris::PrimitiveType::TYPE_OBJECT;
         type = std::make_shared<DataTypeBitMap>();
         return 1;
+    case TypeIndex::IPv4:
+        desc.type = doris::PrimitiveType::TYPE_IPV4;
+        type = std::make_shared<DataTypeIPv4>();
+        return 1;
+    case TypeIndex::IPv6:
+        desc.type = doris::PrimitiveType::TYPE_IPV6;
+        type = std::make_shared<DataTypeIPv6>();
+        return 1;
     case TypeIndex::UInt8:
         desc.type = doris::PrimitiveType::TYPE_BOOLEAN;
         type = std::make_shared<DataTypeUInt8>();
@@ -242,6 +252,12 @@ bool insert_cell(MutableColumnPtr& column, DataTypePtr 
type_ptr, const AnyType&
     } else if (type.idx == TypeIndex::BitMap) {
         BitmapValue* bitmap = any_cast<BitmapValue*>(cell);
         column->insert_data((char*)bitmap, sizeof(BitmapValue));
+    } else if (type.is_ipv4()) {
+        auto value = any_cast<ut_type::IPV4>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
+    } else if (type.is_ipv6()) {
+        auto value = any_cast<ut_type::IPV6>(cell);
+        column->insert_data(reinterpret_cast<char*>(&value), 0);
     } else if (type.is_uint8()) {
         auto value = any_cast<ut_type::BOOLEAN>(cell);
         column->insert_data(reinterpret_cast<char*>(&value), 0);
diff --git a/be/test/vec/function/function_test_util.h 
b/be/test/vec/function/function_test_util.h
index d7061ca6076..62666d7c67c 100644
--- a/be/test/vec/function/function_test_util.h
+++ b/be/test/vec/function/function_test_util.h
@@ -104,6 +104,9 @@ using STRING = std::string;
 using DOUBLE = double;
 using FLOAT = float;
 
+using IPV4 = uint32_t;
+using IPV6 = uint128_t;
+
 inline auto DECIMAL = Decimal128V2::double_to_decimal;
 inline auto DECIMALFIELD = [](double v) {
     return DecimalField<Decimal128V2>(Decimal128V2::double_to_decimal(v), 9);
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 c47f2f62c99..0f0dc8b5d2a 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
@@ -132,6 +132,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentCatalo
 import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentDate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentTime;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentUser;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.CutIpv6;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Database;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Date;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.DateDiff;
@@ -213,6 +214,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4NumToStri
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToNum;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToNumOrDefault;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToNumOrNull;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4ToIpv6;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6CIDRToRange;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6NumToString;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6StringToNum;
@@ -579,6 +581,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(CurrentDate.class, "curdate", "current_date"),
             scalar(CurrentTime.class, "curtime", "current_time"),
             scalar(CurrentUser.class, "current_user"),
+            scalar(CutIpv6.class, "cut_ipv6"),
             scalar(Database.class, "database", "schema"),
             scalar(Date.class, "date"),
             scalar(DateDiff.class, "datediff"),
@@ -656,6 +659,7 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(Ipv4StringToNum.class, "ipv4_string_to_num"),
             scalar(Ipv4StringToNumOrDefault.class, 
"ipv4_string_to_num_or_default"),
             scalar(Ipv4StringToNumOrNull.class, "ipv4_string_to_num_or_null", 
"inet_aton"),
+            scalar(Ipv4ToIpv6.class, "ipv4_to_ipv6"),
             scalar(Ipv6NumToString.class, "ipv6_num_to_string", "inet6_ntoa"),
             scalar(Ipv6StringToNum.class, "ipv6_string_to_num"),
             scalar(Ipv6StringToNumOrDefault.class, 
"ipv6_string_to_num_or_default"),
@@ -667,12 +671,6 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(IsIpAddressInRange.class, "is_ip_address_in_range"),
             scalar(Ipv4CIDRToRange.class, "ipv4_cidr_to_range"),
             scalar(Ipv6CIDRToRange.class, "ipv6_cidr_to_range"),
-            scalar(ToIpv4.class, "to_ipv4"),
-            scalar(ToIpv4OrDefault.class, "to_ipv4_or_default"),
-            scalar(ToIpv4OrNull.class, "to_ipv4_or_null"),
-            scalar(ToIpv6.class, "to_ipv6"),
-            scalar(ToIpv6OrDefault.class, "to_ipv6_or_default"),
-            scalar(ToIpv6OrNull.class, "to_ipv6_or_null"),
             scalar(JsonArray.class, "json_array"),
             scalar(JsonObject.class, "json_object"),
             scalar(JsonQuote.class, "json_quote"),
@@ -888,6 +886,12 @@ public class BuiltinScalarFunctions implements 
FunctionHelper {
             scalar(ToDate.class, "to_date"),
             scalar(ToDateV2.class, "to_datev2"),
             scalar(ToDays.class, "to_days"),
+            scalar(ToIpv4.class, "to_ipv4"),
+            scalar(ToIpv4OrDefault.class, "to_ipv4_or_default"),
+            scalar(ToIpv4OrNull.class, "to_ipv4_or_null"),
+            scalar(ToIpv6.class, "to_ipv6"),
+            scalar(ToIpv6OrDefault.class, "to_ipv6_or_default"),
+            scalar(ToIpv6OrNull.class, "to_ipv6_or_null"),
             scalar(Tokenize.class, "tokenize"),
             scalar(ToMonday.class, "to_monday"),
             scalar(ToQuantileState.class, "to_quantile_state"),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CutIpv6.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CutIpv6.java
new file mode 100644
index 00000000000..b60f4a13258
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CutIpv6.java
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.IPv6Type;
+import org.apache.doris.nereids.types.TinyIntType;
+import org.apache.doris.nereids.types.VarcharType;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * scalar function cut_ipv6
+ */
+public class CutIpv6 extends ScalarFunction
+        implements TernaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(VarcharType.SYSTEM_DEFAULT)
+                .args(IPv6Type.INSTANCE, TinyIntType.INSTANCE, 
TinyIntType.INSTANCE));
+
+    public CutIpv6(Expression arg0, Expression arg1, Expression arg2) {
+        super("cut_ipv6", arg0, arg1, arg2);
+    }
+
+    @Override
+    public CutIpv6 withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 3,
+                "cut_ipv6 accept 3 args, but got %s (%s)",
+                children.size(),
+                children);
+        return new CutIpv6(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.visitCutIpv6(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Ipv4ToIpv6.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Ipv4ToIpv6.java
new file mode 100644
index 00000000000..f95e1d30820
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Ipv4ToIpv6.java
@@ -0,0 +1,65 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
+import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.types.IPv4Type;
+import org.apache.doris.nereids.types.IPv6Type;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * scalar function ipv4_to_ipv6
+ */
+public class Ipv4ToIpv6 extends ScalarFunction
+        implements UnaryExpression, ExplicitlyCastableSignature, 
PropagateNullable {
+
+    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
+            FunctionSignature.ret(IPv6Type.INSTANCE).args(IPv4Type.INSTANCE));
+
+    public Ipv4ToIpv6(Expression arg0) {
+        super("ipv4_to_ipv6", arg0);
+    }
+
+    @Override
+    public Ipv4ToIpv6 withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1,
+                "ipv4_to_ipv6 accept 1 args, but got %s (%s)",
+                children.size(),
+                children);
+        return new Ipv4ToIpv6(children.get(0));
+    }
+
+    @Override
+    public List<FunctionSignature> getSignatures() {
+        return SIGNATURES;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitIpv4ToIpv6(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 6413b9dbe47..2f5ef3faa85 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
@@ -139,6 +139,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentCatalo
 import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentDate;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentTime;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentUser;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.CutIpv6;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Database;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.Date;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.DateDiff;
@@ -216,6 +217,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4NumToStri
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToNum;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToNumOrDefault;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToNumOrNull;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4ToIpv6;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6CIDRToRange;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6NumToString;
 import 
org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6StringToNum;
@@ -934,6 +936,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(currentUser, context);
     }
 
+    default R visitCutIpv6(CutIpv6 cutIpv6, C context) {
+        return visitScalarFunction(cutIpv6, context);
+    }
+
     default R visitDatabase(Database database, C context) {
         return visitScalarFunction(database, context);
     }
@@ -1266,6 +1272,10 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(ipv4StringToNumOrNull, context);
     }
 
+    default R visitIpv4ToIpv6(Ipv4ToIpv6 ipv4ToIpv6, C context) {
+        return visitScalarFunction(ipv4ToIpv6, context);
+    }
+
     default R visitIpv6NumToString(Ipv6NumToString ipv6NumToString, C context) 
{
         return visitScalarFunction(ipv6NumToString, context);
     }
@@ -1310,30 +1320,6 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(ipv6CIDRToRange, context);
     }
 
-    default R visitToIpv4(ToIpv4 toIpv4, C context) {
-        return visitScalarFunction(toIpv4, context);
-    }
-
-    default R visitToIpv4OrDefault(ToIpv4OrDefault toIpv4OrDefault, C context) 
{
-        return visitScalarFunction(toIpv4OrDefault, context);
-    }
-
-    default R visitToIpv4OrNull(ToIpv4OrNull toIpv4OrNull, C context) {
-        return visitScalarFunction(toIpv4OrNull, context);
-    }
-
-    default R visitToIpv6(ToIpv6 toIpv6, C context) {
-        return visitScalarFunction(toIpv6, context);
-    }
-
-    default R visitToIpv6OrDefault(ToIpv6OrDefault toIpv6OrDefault, C context) 
{
-        return visitScalarFunction(toIpv6OrDefault, context);
-    }
-
-    default R visitToIpv6OrNull(ToIpv6OrNull toIpv6OrNull, C context) {
-        return visitScalarFunction(toIpv6OrNull, context);
-    }
-
     default R visitJsonArray(JsonArray jsonArray, C context) {
         return visitScalarFunction(jsonArray, context);
     }
@@ -2018,6 +2004,30 @@ public interface ScalarFunctionVisitor<R, C> {
         return visitScalarFunction(toDays, context);
     }
 
+    default R visitToIpv4(ToIpv4 toIpv4, C context) {
+        return visitScalarFunction(toIpv4, context);
+    }
+
+    default R visitToIpv4OrDefault(ToIpv4OrDefault toIpv4OrDefault, C context) 
{
+        return visitScalarFunction(toIpv4OrDefault, context);
+    }
+
+    default R visitToIpv4OrNull(ToIpv4OrNull toIpv4OrNull, C context) {
+        return visitScalarFunction(toIpv4OrNull, context);
+    }
+
+    default R visitToIpv6(ToIpv6 toIpv6, C context) {
+        return visitScalarFunction(toIpv6, context);
+    }
+
+    default R visitToIpv6OrDefault(ToIpv6OrDefault toIpv6OrDefault, C context) 
{
+        return visitScalarFunction(toIpv6OrDefault, context);
+    }
+
+    default R visitToIpv6OrNull(ToIpv6OrNull toIpv6OrNull, C context) {
+        return visitScalarFunction(toIpv6OrNull, context);
+    }
+
     default R visitToMonday(ToMonday toMonday, C context) {
         return visitScalarFunction(toMonday, context);
     }
diff --git a/gensrc/script/doris_builtins_functions.py 
b/gensrc/script/doris_builtins_functions.py
index 81c502d301d..00e81277257 100644
--- a/gensrc/script/doris_builtins_functions.py
+++ b/gensrc/script/doris_builtins_functions.py
@@ -2129,6 +2129,8 @@ visible_functions = {
         [['to_ipv6_or_default'], 'IPV6', ['STRING'], 'ALWAYS_NOT_NULLABLE'],
         [['to_ipv6_or_null'], 'IPV6', ['VARCHAR'], 'ALWAYS_NULLABLE'],
         [['to_ipv6_or_null'], 'IPV6', ['STRING'], 'ALWAYS_NULLABLE'],
+        [['ipv4_to_ipv6'], 'IPV6', ['IPV4'], 'DEPEND_ON_ARGUMENT'],
+        [['cut_ipv6'], 'STRING', ['IPV6', 'TINYINT', 'TINYINT'], 
'DEPEND_ON_ARGUMENT'],
     ],
 
     "NonNullalbe": [
diff --git 
a/regression-test/data/query_p0/sql_functions/ip_functions/test_cut_ipv6_function.out
 
b/regression-test/data/query_p0/sql_functions/ip_functions/test_cut_ipv6_function.out
new file mode 100644
index 00000000000..c549c1533cd
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/ip_functions/test_cut_ipv6_function.out
@@ -0,0 +1,19 @@
+-- !sql --
+::     ::ffff:0.0.0.0
+::     ::ffff:192.168.0.0
+2001:1b70:a1:610::     ::ffff:0.0.0.0
+ffff:ffff:ffff:ffff::  ::
+::     ::
+::     \N
+\N     ::
+\N     ::
+::     \N
+
+-- !sql --
+\N
+
+-- !sql --
+\N
+
+-- !sql --
+\N
diff --git 
a/regression-test/data/query_p0/sql_functions/ip_functions/test_ipv4_to_ipv6_function.out
 
b/regression-test/data/query_p0/sql_functions/ip_functions/test_ipv4_to_ipv6_function.out
new file mode 100644
index 00000000000..8ae40a99c15
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/ip_functions/test_ipv4_to_ipv6_function.out
@@ -0,0 +1,14 @@
+-- !sql --
+::ffff:0.0.0.0
+::ffff:0.0.0.1
+::ffff:127.0.0.1
+::ffff:192.168.0.1
+::ffff:255.255.255.254
+::ffff:255.255.255.255
+\N
+
+-- !sql --
+::ffff:192.168.0.1
+
+-- !sql --
+\N
diff --git 
a/regression-test/suites/query_p0/sql_functions/ip_functions/test_cut_ipv6_function.groovy
 
b/regression-test/suites/query_p0/sql_functions/ip_functions/test_cut_ipv6_function.groovy
new file mode 100644
index 00000000000..099705e2383
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/ip_functions/test_cut_ipv6_function.groovy
@@ -0,0 +1,56 @@
+// 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_cut_ipv6_function") {
+    sql """ DROP TABLE IF EXISTS test_cut_ipv6_function """
+
+    sql """ SET enable_nereids_planner=true """
+    sql """ SET enable_fallback_to_original_planner=false """
+
+    sql """
+    CREATE TABLE `test_cut_ipv6_function` (
+      `id` int,
+      `ip_v4` string,
+      `ip_v6` string,
+      `bytes_for_ipv6` tinyint,
+      `bytes_for_ipv4` tinyint
+    ) ENGINE=OLAP
+    DISTRIBUTED BY HASH(`id`) BUCKETS 4
+    PROPERTIES (
+    "replication_allocation" = "tag.location.default: 1"
+    );
+    """
+    sql """
+    insert into test_cut_ipv6_function values
+    (1, '0.0.0.0', '::', 0, 0),
+    (2, '192.168.0.1', '::1', 2, 2),
+    (3, '127.0.0.1', '2001:1b70:a1:610::b102:2', 4, 4),
+    (4, '255.255.255.254', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe', 8, 8),
+    (5, '255.255.255.255', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 16, 16),
+    (6, NULL, 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 16, 16),
+    (7, '255.255.255.255', NULL, 16, 16),
+    (8, '255.255.255.255', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', NULL, 
16),
+    (9, '255.255.255.255', 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 16, NULL)
+    """
+
+    qt_sql "select cut_ipv6(to_ipv6_or_null(ip_v6), bytes_for_ipv6, 0), 
cut_ipv6(ipv4_to_ipv6(to_ipv4_or_null(ip_v4)), 0, bytes_for_ipv4) from 
test_cut_ipv6_function order by id"
+
+    qt_sql "select cut_ipv6(NULL, 0, 0)"
+    qt_sql "select 
cut_ipv6(to_ipv6('2001:0DB8:AC10:FE01:FEED:BABE:CAFE:F00D'), NULL, 0)"
+    qt_sql "select 
cut_ipv6(to_ipv6('2001:0DB8:AC10:FE01:FEED:BABE:CAFE:F00D'), 0, NULL)"
+
+    sql "DROP TABLE test_cut_ipv6_function"
+}
\ No newline at end of file
diff --git 
a/regression-test/suites/query_p0/sql_functions/ip_functions/test_ipv4_to_ipv6_function.groovy
 
b/regression-test/suites/query_p0/sql_functions/ip_functions/test_ipv4_to_ipv6_function.groovy
new file mode 100644
index 00000000000..78fd53aacfd
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/ip_functions/test_ipv4_to_ipv6_function.groovy
@@ -0,0 +1,51 @@
+// 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_ipv4_to_ipv6_function") {
+    sql """ DROP TABLE IF EXISTS test_ipv4_to_ipv6_function """
+
+    sql """ SET enable_nereids_planner=true """
+    sql """ SET enable_fallback_to_original_planner=false """
+
+    sql """
+    CREATE TABLE `test_ipv4_to_ipv6_function` (
+      `id` int,
+      `ip_v4` string
+    ) ENGINE=OLAP
+    DISTRIBUTED BY HASH(`id`) BUCKETS 4
+    PROPERTIES (
+    "replication_allocation" = "tag.location.default: 1"
+    );
+    """
+    sql """
+    insert into test_ipv4_to_ipv6_function values
+    (1, '0.0.0.0'),
+    (2, '0.0.0.1'),
+    (3, '127.0.0.1'),
+    (4, '192.168.0.1'),
+    (5, '255.255.255.254'),
+    (6, '255.255.255.255'),
+    (7, NULL)
+    """
+
+    qt_sql "select ipv6_num_to_string(ipv4_to_ipv6(to_ipv4_or_null(ip_v4))) 
from test_ipv4_to_ipv6_function order by id"
+
+    qt_sql """ select ipv6_num_to_string(ipv4_to_ipv6(to_ipv4('192.168.0.1'))) 
"""
+
+    qt_sql "select ipv4_to_ipv6(NULL)"
+
+    sql "DROP TABLE test_ipv4_to_ipv6_function"
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org


Reply via email to