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