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 6ddff3dec12 [improve](ipv6) Enhance ipv6 type to accept uint128 strings in netwo… (#48802) 6ddff3dec12 is described below commit 6ddff3dec124c3148bfb73e5fd175795e0e10a06 Author: amory <wangqian...@selectdb.com> AuthorDate: Mon Mar 10 11:30:30 2025 +0800 [improve](ipv6) Enhance ipv6 type to accept uint128 strings in netwo… (#48802) …rk byte order (#47126) ### What problem does this PR solve? Enhance ipv6 type to accept uint128 strings in network byte order. according to function : ipv6_from_uint128_string_or_null(string) For load we can load the uint128 number like ```338288524927261089657673092949903306134``` using function ipv6_from_uint128_string_or_null or just using function in select sql ``` mysql> select ipv6_from_uint128_string_or_null('340282366920938463463374607431768211455'); +-----------------------------------------------------------------------------+ | ipv6_from_uint128_string_or_null('340282366920938463463374607431768211455') | +-----------------------------------------------------------------------------+ | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | +-----------------------------------------------------------------------------+ 1 row in set (0.06 sec) ```、 --- be/src/vec/functions/function_ip.cpp | 3 + be/src/vec/functions/function_ip.h | 44 +++++++++ be/src/vec/runtime/ipv6_value.h | 11 +++ .../doris/catalog/BuiltinScalarFunctions.java | 2 + .../scalar/Ipv6FromUInt128StringOrNull.java | 70 ++++++++++++++ .../expressions/visitor/ScalarFunctionVisitor.java | 5 + gensrc/script/doris_builtins_functions.py | 2 + .../data/datatype_p0/ip/test_data/ipv6_uint128.csv | 105 +++++++++++++++++++++ .../data/datatype_p0/ip/test_ip_basic.out | Bin 9441 -> 15062 bytes .../suites/datatype_p0/ip/test_ip_basic.groovy | 50 ++++++++++ 10 files changed, 292 insertions(+) diff --git a/be/src/vec/functions/function_ip.cpp b/be/src/vec/functions/function_ip.cpp index 30b31901624..3b5782865fb 100644 --- a/be/src/vec/functions/function_ip.cpp +++ b/be/src/vec/functions/function_ip.cpp @@ -66,5 +66,8 @@ void register_function_ip(SimpleFunctionFactory& factory) { /// Cut IPv6 part factory.register_function<FunctionCutIPv6>(); + + // Covert to IPv6 from uint128-string + factory.register_function<FunctionIPv6FromUInt128StringOrNull>(); } } // 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 a212c4130ca..00b43955372 100644 --- a/be/src/vec/functions/function_ip.h +++ b/be/src/vec/functions/function_ip.h @@ -1405,4 +1405,48 @@ private: } }; +class FunctionIPv6FromUInt128StringOrNull : public IFunction { +public: + static constexpr auto name = "ipv6_from_uint128_string_or_null"; + static FunctionPtr create() { return std::make_shared<FunctionIPv6FromUInt128StringOrNull>(); } + + 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<DataTypeNullable>(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& ipv6_column_with_type_and_name = block.get_by_position(arguments[0]); + const auto& [ipv6_column, ipv6_const] = + unpack_if_const(ipv6_column_with_type_and_name.column); + const auto* ipv6_addr_column = assert_cast<const ColumnString*>(ipv6_column.get()); + // result is nullable column + auto col_res = ColumnNullable::create(ColumnIPv6::create(input_rows_count, 0), + ColumnUInt8::create(input_rows_count, 1)); + auto& col_res_data = assert_cast<ColumnIPv6*>(&col_res->get_nested_column())->get_data(); + auto& res_null_map_data = col_res->get_null_map_data(); + + for (size_t i = 0; i < input_rows_count; ++i) { + IPv6 ipv6 = 0; + auto ipv6_idx = index_check_const(i, ipv6_const); + StringRef uint128_string = ipv6_addr_column->get_data_at(ipv6_idx); + if (!IPv6Value::from_uint128_string(ipv6, uint128_string.data, uint128_string.size)) { + VLOG_DEBUG << "Invalid uin128 IPv6 value '" << uint128_string.to_string_view() + << "'"; + // we should set null to the result not throw exception for load senior + } else { + col_res_data[i] = ipv6; + res_null_map_data[i] = 0; + } + } + + block.replace_by_position(result, std::move(col_res)); + return Status::OK(); + } +}; + } // namespace doris::vectorized diff --git a/be/src/vec/runtime/ipv6_value.h b/be/src/vec/runtime/ipv6_value.h index 5a26af25243..d4ea45576c3 100644 --- a/be/src/vec/runtime/ipv6_value.h +++ b/be/src/vec/runtime/ipv6_value.h @@ -42,6 +42,17 @@ public: bool from_string(const std::string& ipv6_str) { return from_string(_value, ipv6_str); } + static bool from_uint128_string(IPv6& value, const char* ipv6_str, size_t len) { + value = 0; + for (size_t i = 0; i < len; ++i) { + if (ipv6_str[i] < '0' || ipv6_str[i] > '9') { + return false; // illegal character for uint128 + } + value = value * 10 + (ipv6_str[i] - '0'); + } + return true; + } + static bool from_string(IPv6& value, const char* ipv6_str, size_t len) { if (len == 0) { return false; 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 e2646a0b8c3..0b13af6b68d 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 @@ -222,6 +222,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToN 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.Ipv6FromUInt128StringOrNull; import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6NumToString; import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6StringToNum; import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6StringToNumOrDefault; @@ -693,6 +694,7 @@ 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(Ipv6FromUInt128StringOrNull.class, "ipv6_from_uint128_string_or_null"), scalar(JsonArray.class, "json_array"), scalar(JsonObject.class, "json_object"), scalar(JsonQuote.class, "json_quote"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Ipv6FromUInt128StringOrNull.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Ipv6FromUInt128StringOrNull.java new file mode 100644 index 00000000000..6c37549d68d --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Ipv6FromUInt128StringOrNull.java @@ -0,0 +1,70 @@ +// 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.UnaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.IPv6Type; +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; + +/** + * scalar function ipv6_from_uint128_string_or_null + * args just accept varchar as uint128 string + * return ipv6 or null + * sql : select ipv6_from_uint128_string_or_null('340282366920938463463374607431768211455'); + */ +public class Ipv6FromUInt128StringOrNull extends ScalarFunction + implements UnaryExpression, ExplicitlyCastableSignature, AlwaysNullable { + + public static final List<FunctionSignature> SIGNATURES = ImmutableList.of( + FunctionSignature.ret(IPv6Type.INSTANCE).args(VarcharType.SYSTEM_DEFAULT), + FunctionSignature.ret(IPv6Type.INSTANCE).args(StringType.INSTANCE)); + + public Ipv6FromUInt128StringOrNull(Expression arg0) { + super("ipv6_from_uint128_string_or_null", arg0); + } + + @Override + public Ipv6FromUInt128StringOrNull withChildren(List<Expression> children) { + Preconditions.checkArgument(children.size() == 1, + "ipv6_from_uint128_string_or_null accept 1 args, but got %s (%s)", + children.size(), + children); + return new Ipv6FromUInt128StringOrNull(children.get(0)); + } + + @Override + public List<FunctionSignature> getSignatures() { + return SIGNATURES; + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitIpv6FromUInt128StringOrNull(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 b740bb3a125..b74c80e5775 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 @@ -225,6 +225,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv4StringToN 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.Ipv6FromUInt128StringOrNull; import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6NumToString; import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6StringToNum; import org.apache.doris.nereids.trees.expressions.functions.scalar.Ipv6StringToNumOrDefault; @@ -1334,6 +1335,10 @@ public interface ScalarFunctionVisitor<R, C> { return visitScalarFunction(ipv6StringToNumOrNull, context); } + default R visitIpv6FromUInt128StringOrNull(Ipv6FromUInt128StringOrNull ipv6FromUInt128StringOrNull, C context) { + return visitScalarFunction(ipv6FromUInt128StringOrNull, context); + } + default R visitIsIpv4Compat(IsIpv4Compat isIpv4Compat, C context) { return visitScalarFunction(isIpv4Compat, context); } diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 6d27d4cbe97..0cd74f99550 100644 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -2142,6 +2142,8 @@ visible_functions = { [['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'], + [['ipv6_from_uint128_string_or_null'], 'IPV6', ['VARCHAR'], 'ALWAYS_NULLABLE'], + [['ipv6_from_uint128_string_or_null'], 'IPV6', ['STRING'], 'ALWAYS_NULLABLE'] ], "NonNullalbe": [ diff --git a/regression-test/data/datatype_p0/ip/test_data/ipv6_uint128.csv b/regression-test/data/datatype_p0/ip/test_data/ipv6_uint128.csv new file mode 100644 index 00000000000..1ae0cc42e1f --- /dev/null +++ b/regression-test/data/datatype_p0/ip/test_data/ipv6_uint128.csv @@ -0,0 +1,105 @@ +1,42540766411282592856903984951653826560,2001:db8:: +2,42540766411282592856903984951653826561,2001:db8::1 +3,42540766411282592856903984951653826562,2001:db8::2 +4,42540766411282592856903984951653826563,2001:db8::3 +5,42540766411282592856903984951653826564,2001:db8::4 +6,42540766411282592856903984951653826565,2001:db8::5 +7,42540766411282592856903984951653826566,2001:db8::6 +8,42540766411282592856903984951653826567,2001:db8::7 +9,42540766411282592856903984951653826568,2001:db8::8 +10,42540766411282592856903984951653826569,2001:db8::9 +11,42540766411282592856903984951653826570,2001:db8::a +12,42540766411282592856903984951653826571,2001:db8::b +13,42540766411282592856903984951653826572,2001:db8::c +14,42540766411282592856903984951653826573,2001:db8::d +15,42540766411282592856903984951653826574,2001:db8::e +16,42540766411282592856903984951653826575,2001:db8::f +17,42540766411282592856903984951653826576,2001:db8::10 +18,42540766411282592856903984951653826577,2001:db8::11 +19,42540766411282592856903984951653826578,2001:db8::12 +20,42540766411282592856903984951653826579,2001:db8::13 +21,42540766411282592856903984951653826580,2001:db8::14 +22,42540766411282592856903984951653826581,2001:db8::15 +23,42540766411282592856903984951653826582,2001:db8::16 +24,42540766411282592856903984951653826583,2001:db8::17 +25,42540766411282592856903984951653826584,2001:db8::18 +26,42540766411282592856903984951653826585,2001:db8::19 +27,42540766411282592856903984951653826586,2001:db8::1a +28,42540766411282592856903984951653826587,2001:db8::1b +29,42540766411282592856903984951653826588,2001:db8::1c +30,42540766411282592856903984951653826589,2001:db8::1d +31,42540766411282592856903984951653826590,2001:db8::1e +32,42540766411282592856903984951653826591,2001:db8::1f +33,42540766411282592856903984951653826592,2001:db8::20 +34,42540766411282592856903984951653826593,2001:db8::21 +35,42540766411282592856903984951653826594,2001:db8::22 +36,42540766411282592856903984951653826595,2001:db8::23 +37,42540766411282592856903984951653826596,2001:db8::24 +38,42540766411282592856903984951653826597,2001:db8::25 +39,42540766411282592856903984951653826598,2001:db8::26 +40,42540766411282592856903984951653826599,2001:db8::27 +41,42540766411282592856903984951653826600,2001:db8::28 +42,42540766411282592856903984951653826601,2001:db8::29 +43,42540766411282592856903984951653826602,2001:db8::2a +44,42540766411282592856903984951653826603,2001:db8::2b +45,42540766411282592856903984951653826604,2001:db8::2c +46,42540766411282592856903984951653826605,2001:db8::2d +47,42540766411282592856903984951653826606,2001:db8::2e +48,42540766411282592856903984951653826607,2001:db8::2f +49,42540766411282592856903984951653826608,2001:db8::30 +50,42540766411282592856903984951653826609,2001:db8::31 +51,42540766411282592856903984951653826610,2001:db8::32 +52,42540766411282592856903984951653826611,2001:db8::33 +53,42540766411282592856903984951653826612,2001:db8::34 +54,42540766411282592856903984951653826613,2001:db8::35 +55,42540766411282592856903984951653826614,2001:db8::36 +56,42540766411282592856903984951653826615,2001:db8::37 +57,42540766411282592856903984951653826616,2001:db8::38 +58,42540766411282592856903984951653826617,2001:db8::39 +59,42540766411282592856903984951653826618,2001:db8::3a +60,42540766411282592856903984951653826619,2001:db8::3b +61,42540766411282592856903984951653826620,2001:db8::3c +62,42540766411282592856903984951653826621,2001:db8::3d +63,42540766411282592856903984951653826622,2001:db8::3e +64,42540766411282592856903984951653826623,2001:db8::3f +65,42540766411282592856903984951653826624,2001:db8::40 +66,42540766411282592856903984951653826625,2001:db8::41 +67,42540766411282592856903984951653826626,2001:db8::42 +68,42540766411282592856903984951653826627,2001:db8::43 +69,42540766411282592856903984951653826628,2001:db8::44 +70,42540766411282592856903984951653826629,2001:db8::45 +71,42540766411282592856903984951653826630,2001:db8::46 +72,42540766411282592856903984951653826631,2001:db8::47 +73,42540766411282592856903984951653826632,2001:db8::48 +74,42540766411282592856903984951653826633,2001:db8::49 +75,42540766411282592856903984951653826634,2001:db8::4a +76,42540766411282592856903984951653826635,2001:db8::4b +77,42540766411282592856903984951653826636,2001:db8::4c +78,42540766411282592856903984951653826637,2001:db8::4d +79,42540766411282592856903984951653826638,2001:db8::4e +80,42540766411282592856903984951653826639,2001:db8::4f +81,42540766411282592856903984951653826640,2001:db8::50 +82,42540766411282592856903984951653826641,2001:db8::51 +83,42540766411282592856903984951653826642,2001:db8::52 +84,42540766411282592856903984951653826643,2001:db8::53 +85,42540766411282592856903984951653826644,2001:db8::54 +86,42540766411282592856903984951653826645,2001:db8::55 +87,42540766411282592856903984951653826646,2001:db8::56 +88,42540766411282592856903984951653826647,2001:db8::57 +89,42540766411282592856903984951653826648,2001:db8::58 +90,42540766411282592856903984951653826649,2001:db8::59 +91,42540766411282592856903984951653826650,2001:db8::5a +92,42540766411282592856903984951653826651,2001:db8::5b +93,42540766411282592856903984951653826652,2001:db8::5c +94,42540766411282592856903984951653826653,2001:db8::5d +95,42540766411282592856903984951653826654,2001:db8::5e +96,42540766411282592856903984951653826655,2001:db8::5f +97,42540766411282592856903984951653826656,2001:db8::60 +98,42540766411282592856903984951653826657,2001:db8::61 +99,42540766411282592856903984951653826658,2001:db8::62 +100,42540766411282592856903984951653826659,2001:db8::63 +101,1,::1 +102,0,:: +103,338963523518870617245727861364146307073,ff02::1 +104,338963523518870617245727861364146307074,ff02::2 +105,42540766411282592856903984951653826560,2001:db8:: \ No newline at end of file diff --git a/regression-test/data/datatype_p0/ip/test_ip_basic.out b/regression-test/data/datatype_p0/ip/test_ip_basic.out index e1b85abe00c..6a822b7cde6 100644 Binary files a/regression-test/data/datatype_p0/ip/test_ip_basic.out and b/regression-test/data/datatype_p0/ip/test_ip_basic.out differ diff --git a/regression-test/suites/datatype_p0/ip/test_ip_basic.groovy b/regression-test/suites/datatype_p0/ip/test_ip_basic.groovy index 5a4ab2ca94a..caf6ab02870 100644 --- a/regression-test/suites/datatype_p0/ip/test_ip_basic.groovy +++ b/regression-test/suites/datatype_p0/ip/test_ip_basic.groovy @@ -186,4 +186,54 @@ suite("test_ip_basic") { qt_sql """ alter table table_ip_default_like add column col25 ipv6 NULL DEFAULT "::" """ qt_sql """ alter table table_ip_default_like add column col26 ipv4 NULL DEFAULT "127.0.0.1" """ qt_sql """ select * from table_ip_default_like order by col0 """ + + // test ipv6 with uint128 presentation + sql """ DROP TABLE IF EXISTS table_ipv6_uint128 """ + sql """ CREATE TABLE IF NOT EXISTS `table_ipv6_uint128` (`col0` bigint NOT NULL, `ipv6_uint128` ipv6 NOT NULL, `ipv6` ipv6 NOT NULL) ENGINE=OLAP UNIQUE KEY(`col0`) DISTRIBUTED BY HASH(`col0`) BUCKETS 4 PROPERTIES ("replication_allocation" = "tag.location.default: 1") """ + // streamload + streamLoad { + db 'regression_test_datatype_p0_ip' + table 'table_ipv6_uint128' + set 'column_separator', ',' + set 'columns', 'col0, tmp, ipv6, ipv6_uint128=ipv6_from_uint128_string_or_null(tmp)' + file 'test_data/ipv6_uint128.csv' + time 10000 // limit inflight 10s + // stream load action will check result, include Success status, and NumberTotalRows == NumberLoadedRows + check { res, exception, startTime, endTime -> + if (exception != null) { + throw exception + } + log.info("Stream load result: ${res}".toString()) + def json = parseJson(res) + assertEquals("success", json.Status.toLowerCase()) + assertEquals(json.NumberTotalRows, json.NumberLoadedRows) + assertTrue(json.LoadBytes > 0) + } + } + qt_sql_ipv6 """ select * from table_ipv6_uint128 order by col0 """ + // test cast + sql """ DROP TABLE IF EXISTS table_ipv6_uint128_string """ + sql """ CREATE TABLE IF NOT EXISTS `table_ipv6_uint128_string` (`col0` bigint NOT NULL, `ipv6_uint128` string NOT NULL, `ipv6` ipv6 NOT NULL) ENGINE=OLAP UNIQUE KEY(`col0`) DISTRIBUTED BY HASH(`col0`) BUCKETS 4 PROPERTIES ("replication_allocation" = "tag.location.default: 1") """ + // streamload + streamLoad { + db 'regression_test_datatype_p0_ip' + table 'table_ipv6_uint128_string' + set 'column_separator', ',' + file 'test_data/ipv6_uint128.csv' + time 10000 // limit inflight 10s + // stream load action will check result, include Success status, and NumberTotalRows == NumberLoadedRows + check { res, exception, startTime, endTime -> + if (exception != null) { + throw exception + } + log.info("Stream load result: ${res}".toString()) + def json = parseJson(res) + assertEquals("success", json.Status.toLowerCase()) + assertEquals(json.NumberTotalRows, json.NumberLoadedRows) + assertTrue(json.LoadBytes > 0) + } + } + + // then cast ipv6_uint128 to ipv6 + qt_sql_ipv6_string """ select ipv6_from_uint128_string_or_null(ipv6_uint128) as ipv6_uint128, ipv6 from table_ipv6_uint128_string order by col0 """ } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org