diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/util/NetUtils.java b/fe/fe-core/src/main/java/org/apache/doris/common/util/NetUtils.java index 9b787f52bf47a2..13fa4fbaf9c60f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/util/NetUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/NetUtils.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.net.DatagramSocket; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; @@ -161,4 +162,31 @@ public static SystemInfoService.HostInfo resolveHostInfoFromHostPort(String host return new SystemInfoService.HostInfo(pair[0], Integer.valueOf(pair[1])); } + /** + * Convert IPv4 address to long + * @param inet4Address IPv4 address + * @return The corresponding long value + */ + public static long inet4AddressToLong(Inet4Address inet4Address) { + byte[] bytes = inet4Address.getAddress(); + long result = 0; + for (byte b : bytes) { + result = result << 8 | (b & 0xFF); + } + return result; + } + + /** + * Convert long value back to IPv4 address + * @param value IP address as a long value + * @return The corresponding IPv4 address + */ + public static Inet4Address longToInet4Address(long value) throws Exception { + byte[] bytes = new byte[4]; + bytes[0] = (byte) ((value >> 24) & 0xFF); + bytes[1] = (byte) ((value >> 16) & 0xFF); + bytes[2] = (byte) ((value >> 8) & 0xFF); + bytes[3] = (byte) (value & 0xFF); + return (Inet4Address) Inet4Address.getByAddress(bytes); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IPv4Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IPv4Literal.java index e4e931738b02a2..fd012561260869 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IPv4Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/IPv4Literal.java @@ -18,10 +18,13 @@ package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.common.util.NetUtils; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.IPv4Type; +import java.net.Inet4Address; +import java.util.Objects; import java.util.regex.Pattern; /** @@ -32,20 +35,67 @@ public class IPv4Literal extends Literal { private static final Pattern IPV4_STD_REGEX = Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); - private long value; + /** + * Add a class Inet4Addr wrap in Inet4Address, + * When cast ipv4 literal to string, it will call `new StringLiteral(ipv4Literal.getValue().toString())`, + * but Inet4Address.toString() contains a prefix "/", like "/192.168.1.10". + * Use Inet4Addr can solve this problem. + */ + public static class Inet4Addr { + final Inet4Address address; + + public Inet4Addr(Inet4Address addr) { + this.address = addr; + } + + public Inet4Address getAddress() { + return this.address; + } + + public long toLong() { + return NetUtils.inet4AddressToLong(address); + } + + @Override + public String toString() { + return address.getHostAddress(); + } + + @Override + public int hashCode() { + return Objects.hash(address); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Inet4Addr)) { + return false; + } + Inet4Addr otherAddr = (Inet4Addr) other; + return address.equals(otherAddr.address); + } + } + + private Inet4Addr value; public IPv4Literal(String ipv4) throws AnalysisException { super(IPv4Type.INSTANCE); init(ipv4); } - protected IPv4Literal(long value) { + protected IPv4Literal(long value) throws AnalysisException { super(IPv4Type.INSTANCE); - this.value = value; + Inet4Address address; + try { + address = NetUtils.longToInet4Address(value); + } catch (Exception e) { + throw new AnalysisException(e.getMessage()); + } + this.value = new Inet4Addr(address); } @Override - public Long getValue() { + public Inet4Addr getValue() { return value; } @@ -56,7 +106,7 @@ public R accept(ExpressionVisitor visitor, C context) { @Override public LiteralExpr toLegacyLiteral() { - return new org.apache.doris.analysis.IPv4Literal(value); + return new org.apache.doris.analysis.IPv4Literal(value.toLong()); } void init(String ipv4) throws AnalysisException { @@ -80,7 +130,13 @@ void init(String ipv4) throws AnalysisException { } value = (value << 8) | octet; } - this.value = value; + Inet4Address address; + try { + address = NetUtils.longToInet4Address(value); + } catch (Exception e) { + throw new AnalysisException(e.getMessage()); + } + this.value = new Inet4Addr(address); } private void checkValueValid(String ipv4) throws AnalysisException { diff --git a/fe/fe-core/src/test/java/org/apache/doris/common/util/NetUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/common/util/NetUtilsTest.java new file mode 100644 index 00000000000000..bbebfdfd33f7b8 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/common/util/NetUtilsTest.java @@ -0,0 +1,37 @@ +// 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.common.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.net.Inet4Address; +import java.net.InetAddress; + +public class NetUtilsTest { + + @Test + public void testConvertIp() throws Exception { + long ipValue = 3232235786L; + InetAddress ip = InetAddress.getByName("192.168.1.10"); + Assert.assertTrue(ip instanceof Inet4Address); + Assert.assertEquals(ipValue, NetUtils.inet4AddressToLong((Inet4Address) ip)); + Inet4Address convertIp = NetUtils.longToInet4Address(ipValue); + Assert.assertEquals(ip, convertIp); + } +} diff --git a/regression-test/data/nereids_rules_p0/infer_predicate/pull_up_predicate_literal.out b/regression-test/data/nereids_rules_p0/infer_predicate/pull_up_predicate_literal.out index 88d7fa790d9d61..1b4c185e280013 100644 --- a/regression-test/data/nereids_rules_p0/infer_predicate/pull_up_predicate_literal.out +++ b/regression-test/data/nereids_rules_p0/infer_predicate/pull_up_predicate_literal.out @@ -2471,7 +2471,7 @@ PhysicalResultSink ----------PhysicalProject ------------PhysicalStorageLayerAggregate[test_pull_up_predicate_literal] ------PhysicalProject ---------filter((t2.d_ipv4 = 2130706433)) +--------filter((t2.d_ipv4 = 127.0.0.1)) ----------PhysicalOlapScan[test_types] -- !const_value_and_join_column_type239 -- diff --git a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_ip.groovy b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_ip.groovy new file mode 100644 index 00000000000000..375057a01a88a7 --- /dev/null +++ b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_ip.groovy @@ -0,0 +1,30 @@ +// 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("fold_constant_ip") { + // cast function + for (def ipv4 : ["1", "256.256.256.256", "192.168.1.10"]) { + testFoldConst("SELECT cast('${ipv4}' as ipv4)") + testFoldConst("SELECT cast(cast('${ipv4}' as ipv4) as string)") + testFoldConst("SELECT cast(cast(cast('${ipv4}' as ipv4) as string) as ipv4)") + } + for (def ipv6 : ["1", "ef8d:3d6a:869b:2582:7200:aa46:4dcd:2bd4"]) { + testFoldConst("SELECT cast('${ipv6}' as ipv6)") + testFoldConst("SELECT cast(cast('${ipv6}' as ipv6) as string)") + testFoldConst("SELECT cast(cast(cast('${ipv6}' as ipv6) as string) as ipv6)") + } +}