[Feature](datatype) Add IPv4/v6 data type for doris (#24965)

This commit is contained in:
yangshijie
2023-10-26 17:33:28 +08:00
committed by GitHub
parent 9705ec760c
commit c1d64a7128
77 changed files with 2728 additions and 14 deletions

View File

@ -329,6 +329,19 @@ public class BinaryPredicate extends Predicate implements Writable {
}
}
private boolean canCompareIP(PrimitiveType t1, PrimitiveType t2) {
if (t1.isIPv4Type()) {
return t2.isIPv4Type() || t2.isStringType();
} else if (t2.isIPv4Type()) {
return t1.isStringType();
} else if (t1.isIPv6Type()) {
return t2.isIPv6Type() || t2.isStringType();
} else if (t2.isIPv6Type()) {
return t1.isStringType();
}
return false;
}
private Type dateV2ComparisonResultType(ScalarType t1, ScalarType t2) {
if (!t1.isDatetimeV2() && !t2.isDatetimeV2()) {
return Type.DATEV2;
@ -411,6 +424,19 @@ public class BinaryPredicate extends Predicate implements Writable {
}
}
if (canCompareIP(getChild(0).getType().getPrimitiveType(), getChild(1).getType().getPrimitiveType())) {
if ((getChild(0).getType().isIP() && getChild(1) instanceof StringLiteral)
|| (getChild(1).getType().isIP() && getChild(0) instanceof StringLiteral)
|| (getChild(0).getType().isIP() && getChild(1).getType().isIP())) {
if (getChild(0).getType().isIPv4() || getChild(1).getType().isIPv4()) {
return Type.IPV4;
}
if (getChild(0).getType().isIPv6() || getChild(1).getType().isIPv6()) {
return Type.IPV6;
}
}
}
// Following logical is compatible with MySQL:
// Cast to DOUBLE by default, because DOUBLE has the largest range of values.
if (t1 == PrimitiveType.VARCHAR && t2 == PrimitiveType.VARCHAR) {

View File

@ -0,0 +1,146 @@
// 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.analysis;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.thrift.TExprNode;
import org.apache.doris.thrift.TExprNodeType;
import org.apache.doris.thrift.TIPv4Literal;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.regex.Pattern;
public class IPv4Literal extends LiteralExpr {
private static final Logger LOG = LogManager.getLogger(IPv4Literal.class);
public static final long IPV4_MIN = 0L; // 0.0.0.0
public static final long IPV4_MAX = (2L << 31) - 1; // 255.255.255.255
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;
/**
* C'tor forcing type, e.g., due to implicit cast
*/
// for restore
private IPv4Literal() {
}
public IPv4Literal(long value) {
super();
this.value = value;
this.type = Type.IPV4;
analysisDone();
}
public IPv4Literal(String value) throws AnalysisException {
super();
this.value = parseIPv4toLong(value);
this.type = Type.IPV4;
analysisDone();
}
protected IPv4Literal(IPv4Literal other) {
super(other);
this.value = other.value;
}
private static long parseIPv4toLong(String ipv4) {
String[] parts = ipv4.split("\\.");
if (parts.length != 4) {
return 0L;
}
long value = 0L;
for (int i = 0; i < 4; ++i) {
short octet;
try {
octet = Short.parseShort(parts[i]);
} catch (NumberFormatException e) {
return 0L;
}
if (octet < 0 || octet > 255) {
return 0L;
}
value = (value << 8) | octet;
}
return value;
}
private static String parseLongToIPv4(long ipv4) {
StringBuilder sb = new StringBuilder();
for (int i = 3; i >= 0; i--) {
short octet = (short) ((ipv4 >> (i * 8)) & 0xFF);
sb.append(octet);
if (i > 0) {
sb.append(".");
}
}
return sb.toString();
}
private void checkValueValid(String ipv4) throws AnalysisException {
if (ipv4.length() > 15) {
throw new AnalysisException("The length of IPv4 must not exceed 15. type: " + Type.IPV4);
} else if (!IPV4_STD_REGEX.matcher(ipv4).matches()) {
throw new AnalysisException("Invalid IPv4 format: " + ipv4 + ". type: " + Type.IPV4);
}
}
@Override
public Expr clone() {
return new IPv4Literal(this);
}
@Override
protected String toSqlImpl() {
return getStringValue();
}
@Override
protected void toThrift(TExprNode msg) {
msg.node_type = TExprNodeType.IPV4_LITERAL;
msg.ipv4_literal = new TIPv4Literal(this.value);
}
@Override
public boolean isMinValue() {
return this.value == IPV4_MIN;
}
@Override
public int compareLiteral(LiteralExpr expr) {
return 0;
}
@Override
public String getStringValue() {
return parseLongToIPv4(this.value);
}
@Override
public String getStringValueForArray() {
return "\"" + getStringValue() + "\"";
}
}

View File

@ -0,0 +1,111 @@
// 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.analysis;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.thrift.TExprNode;
import org.apache.doris.thrift.TExprNodeType;
import org.apache.doris.thrift.TIPv6Literal;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.regex.Pattern;
public class IPv6Literal extends LiteralExpr {
private static final Logger LOG = LogManager.getLogger(IPv6Literal.class);
public static final String IPV6_MIN = "0:0:0:0:0:0:0:0";
public static final String IPV6_MAX = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
private static final Pattern IPV6_STD_REGEX =
Pattern.compile("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
private static final Pattern IPV6_COMPRESS_REGEX =
Pattern.compile("^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::((([0-9A-Fa-f]{1,4}:)*[0-9A-Fa-f]{1,4})?)$");
private String value;
/**
* C'tor forcing type, e.g., due to implicit cast
*/
// for restore
private IPv6Literal() {
}
public IPv6Literal(String value) throws AnalysisException {
super();
checkValueValid(value);
this.value = value;
this.type = Type.IPV6;
analysisDone();
}
protected IPv6Literal(IPv6Literal other) {
super(other);
this.value = other.value;
}
@Override
public void checkValueValid() throws AnalysisException {
checkValueValid(this.value);
}
private void checkValueValid(String ipv6) throws AnalysisException {
if (ipv6.length() > 39) {
throw new AnalysisException("The length of IPv6 must not exceed 39. type: " + Type.IPV6);
} else if (!IPV6_STD_REGEX.matcher(ipv6).matches() && !IPV6_COMPRESS_REGEX.matcher(ipv6).matches()) {
throw new AnalysisException("Invalid IPv6 format: " + ipv6 + ". type: " + Type.IPV6);
}
}
@Override
protected String toSqlImpl() {
return getStringValue();
}
@Override
protected void toThrift(TExprNode msg) {
msg.node_type = TExprNodeType.IPV6_LITERAL;
msg.ipv6_literal = new TIPv6Literal(this.value);
}
@Override
public Expr clone() {
return new IPv6Literal(this);
}
@Override
public boolean isMinValue() {
return IPV6_MIN.equals(this.value);
}
@Override
public int compareLiteral(LiteralExpr expr) {
return 0;
}
@Override
public String getStringValue() {
return this.value;
}
@Override
public String getStringValueForArray() {
return "\"" + getStringValue() + "\"";
}
}

View File

@ -94,6 +94,12 @@ public abstract class LiteralExpr extends Expr implements Comparable<LiteralExpr
case DATETIMEV2:
literalExpr = new DateLiteral(value, type);
break;
case IPV4:
literalExpr = new IPv4Literal(value);
break;
case IPV6:
literalExpr = new IPv6Literal(value);
break;
default:
throw new AnalysisException("Type[" + type.toSql() + "] not supported.");
}

View File

@ -167,6 +167,40 @@ public class StringLiteral extends LiteralExpr {
return getStringValue();
}
/**
* Convert a string literal to a IPv4 literal
*
* @return new converted literal (not null)
* @throws AnalysisException when entire given string cannot be transformed into a date
*/
public LiteralExpr convertToIPv4() throws AnalysisException {
LiteralExpr newLiteral;
newLiteral = new IPv4Literal(value);
try {
newLiteral.checkValueValid();
} catch (AnalysisException e) {
return NullLiteral.create(newLiteral.getType());
}
return newLiteral;
}
/**
* Convert a string literal to a IPv6 literal
*
* @return new converted literal (not null)
* @throws AnalysisException when entire given string cannot be transformed into a date
*/
public LiteralExpr convertToIPv6() throws AnalysisException {
LiteralExpr newLiteral;
newLiteral = new IPv6Literal(value);
try {
newLiteral.checkValueValid();
} catch (AnalysisException e) {
return NullLiteral.create(newLiteral.getType());
}
return newLiteral;
}
/**
* Convert a string literal to a date literal
*
@ -266,6 +300,10 @@ public class StringLiteral extends LiteralExpr {
} catch (AnalysisException e) {
// pass;
}
} else if (targetType.isIPv4()) {
return convertToIPv4();
} else if (targetType.isIPv6()) {
return convertToIPv6();
} else if (targetType.equals(type)) {
return this;
} else if (targetType.isStringType()) {

View File

@ -0,0 +1,93 @@
// 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.literal;
import org.apache.doris.analysis.LiteralExpr;
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.util.regex.Pattern;
/**
* Represents IPv4 literal
*/
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;
public IPv4Literal(String ipv4) throws AnalysisException {
super(IPv4Type.INSTANCE);
init(ipv4);
}
protected IPv4Literal(long value) {
super(IPv4Type.INSTANCE);
this.value = value;
}
@Override
public Long getValue() {
return value;
}
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitIPv4Literal(this, context);
}
@Override
public LiteralExpr toLegacyLiteral() {
return new org.apache.doris.analysis.IPv4Literal(value);
}
void init(String ipv4) throws AnalysisException {
checkValueValid(ipv4);
String[] parts = ipv4.split("\\.");
if (parts.length != 4) {
return;
}
long value = 0L;
for (int i = 0; i < 4; ++i) {
short octet;
try {
octet = Short.parseShort(parts[i]);
} catch (NumberFormatException e) {
throw new AnalysisException("Invalid IPv4 format.");
}
if (octet < 0 || octet > 255) {
throw new AnalysisException("Invalid IPv4 format.");
}
value = (value << 8) | octet;
}
this.value = value;
}
private void checkValueValid(String ipv4) throws AnalysisException {
if (ipv4.length() > 15) {
throw new AnalysisException("The length of IPv4 must not exceed 15.");
} else if (!IPV4_STD_REGEX.matcher(ipv4).matches()) {
throw new AnalysisException("Invalid IPv4 format.");
}
}
}

View File

@ -0,0 +1,71 @@
// 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.literal;
import org.apache.doris.analysis.LiteralExpr;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.IPv6Type;
import java.util.regex.Pattern;
/**
* Represents IPv6 literal
*/
public class IPv6Literal extends Literal {
private static final Pattern IPV6_STD_REGEX =
Pattern.compile("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
private static final Pattern IPV6_COMPRESS_REGEX =
Pattern.compile("^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::((([0-9A-Fa-f]{1,4}:)*[0-9A-Fa-f]{1,4})?)$");
private final String value;
public IPv6Literal(String ipv6) throws AnalysisException {
super(IPv6Type.INSTANCE);
checkValueValid(ipv6);
this.value = ipv6;
}
@Override
public String getValue() {
return value;
}
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitIPv6Literal(this, context);
}
@Override
public LiteralExpr toLegacyLiteral() {
try {
return new org.apache.doris.analysis.IPv6Literal(value);
} catch (Exception e) {
throw new AnalysisException("Invalid IPv6 format.");
}
}
public void checkValueValid(String ipv6) throws AnalysisException {
if (ipv6.length() > 39) {
throw new AnalysisException("The length of IPv6 must not exceed 39.");
} else if (!IPV6_STD_REGEX.matcher(ipv6).matches() && !IPV6_COMPRESS_REGEX.matcher(ipv6).matches()) {
throw new AnalysisException("Invalid IPv6 format.");
}
}
}

View File

@ -249,6 +249,10 @@ public abstract class Literal extends Expression implements LeafExpression, Comp
return new DateTimeV2Literal((DateTimeV2Type) targetType, desc);
} else if (targetType.isJsonType()) {
return new JsonLiteral(desc);
} else if (targetType.isIPv4Type()) {
return new IPv4Literal(desc);
} else if (targetType.isIPv6Type()) {
return new IPv6Literal(desc);
}
throw new AnalysisException("cannot cast " + desc + " from type " + this.dataType + " to type " + targetType);
}
@ -296,6 +300,10 @@ public abstract class Literal extends Expression implements LeafExpression, Comp
return new DateTimeV2Literal(stringValue);
} else if (dataType.isJsonType()) {
return new JsonLiteral(stringValue);
} else if (dataType.isIPv4Type()) {
return new IPv4Literal(stringValue);
} else if (dataType.isIPv6Type()) {
return new IPv6Literal(stringValue);
} else {
throw new AnalysisException("Unsupported convert the " + literalExpr.getType()
+ " of legacy literal to nereids literal");

View File

@ -100,6 +100,8 @@ import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DecimalV3Literal;
import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
import org.apache.doris.nereids.trees.expressions.literal.FloatLiteral;
import org.apache.doris.nereids.trees.expressions.literal.IPv4Literal;
import org.apache.doris.nereids.trees.expressions.literal.IPv6Literal;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Interval;
import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral;
@ -303,6 +305,14 @@ public abstract class ExpressionVisitor<R, C>
return visitLiteral(dateTimeV2Literal, context);
}
public R visitIPv4Literal(IPv4Literal ipv4Literal, C context) {
return visitLiteral(ipv4Literal, context);
}
public R visitIPv6Literal(IPv6Literal ipv6Literal, C context) {
return visitLiteral(ipv6Literal, context);
}
public R visitArrayLiteral(ArrayLiteral arrayLiteral, C context) {
return visitLiteral(arrayLiteral, context);
}

View File

@ -259,6 +259,12 @@ public abstract class DataType {
case "jsonb":
dataType = JsonType.INSTANCE;
break;
case "ipv4":
dataType = IPv4Type.INSTANCE;
break;
case "ipv6":
dataType = IPv6Type.INSTANCE;
break;
default:
throw new AnalysisException("Nereids do not support type: " + type);
}
@ -358,6 +364,10 @@ public abstract class DataType {
List<DataType> types = catalogType.getSubTypes().stream().map(DataType::fromCatalogType)
.collect(Collectors.toList());
return new AggStateType(catalogType.getFunctionName(), types, catalogType.getSubTypeNullables());
} else if (type.isIPv4()) {
return IPv4Type.INSTANCE;
} else if (type.isIPv6()) {
return IPv6Type.INSTANCE;
} else {
return UnsupportedType.INSTANCE;
}
@ -542,6 +552,14 @@ public abstract class DataType {
return this instanceof DateTimeV2Type;
}
public boolean isIPv4Type() {
return this instanceof IPv4Type;
}
public boolean isIPv6Type() {
return this instanceof IPv6Type;
}
public boolean isBitmapType() {
return this instanceof BitmapType;
}

View File

@ -0,0 +1,69 @@
// 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.types;
import org.apache.doris.catalog.Type;
import org.apache.doris.nereids.types.coercion.PrimitiveType;
/**
* IPv4 type in Nereids.
*/
public class IPv4Type extends PrimitiveType {
public static final IPv4Type INSTANCE = new IPv4Type();
public static final int WIDTH = 4;
private IPv4Type() {
}
@Override
public Type toCatalogDataType() {
return Type.IPV4;
}
@Override
public boolean acceptsType(DataType other) {
return other instanceof IPv4Type;
}
@Override
public String simpleString() {
return "ipv4";
}
@Override
public DataType defaultConcreteType() {
return INSTANCE;
}
@Override
public boolean equals(Object o) {
return o instanceof IPv4Type;
}
@Override
public int width() {
return WIDTH;
}
@Override
public String toSql() {
return "IPV4";
}
}

View File

@ -0,0 +1,69 @@
// 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.types;
import org.apache.doris.catalog.Type;
import org.apache.doris.nereids.types.coercion.PrimitiveType;
/**
* IPv6 type in Nereids.
*/
public class IPv6Type extends PrimitiveType {
public static final IPv6Type INSTANCE = new IPv6Type();
public static final int WIDTH = 16;
private IPv6Type() {
}
@Override
public Type toCatalogDataType() {
return Type.IPV6;
}
@Override
public boolean acceptsType(DataType other) {
return other instanceof IPv6Type;
}
@Override
public String simpleString() {
return "ipv6";
}
@Override
public DataType defaultConcreteType() {
return INSTANCE;
}
@Override
public boolean equals(Object o) {
return o instanceof IPv6Type;
}
@Override
public int width() {
return WIDTH;
}
@Override
public String toSql() {
return "IPV6";
}
}

View File

@ -82,6 +82,8 @@ import org.apache.doris.nereids.types.DecimalV2Type;
import org.apache.doris.nereids.types.DecimalV3Type;
import org.apache.doris.nereids.types.DoubleType;
import org.apache.doris.nereids.types.FloatType;
import org.apache.doris.nereids.types.IPv4Type;
import org.apache.doris.nereids.types.IPv6Type;
import org.apache.doris.nereids.types.IntegerType;
import org.apache.doris.nereids.types.JsonType;
import org.apache.doris.nereids.types.LargeIntType;
@ -227,6 +229,8 @@ public class TypeCoercionUtils {
} else if (expected instanceof NumericType) {
// For any other numeric types, implicitly cast to each other, e.g. bigint -> int, int -> bigint
returnType = expected.defaultConcreteType();
} else if (expected instanceof IPv4Type) {
returnType = IPv4Type.INSTANCE;
}
} else if (input instanceof CharacterType) {
if (expected instanceof DecimalV2Type) {
@ -239,6 +243,10 @@ public class TypeCoercionUtils {
returnType = DateTimeType.INSTANCE;
} else if (expected instanceof JsonType) {
returnType = JsonType.INSTANCE;
} else if (expected instanceof IPv4Type) {
returnType = IPv4Type.INSTANCE;
} else if (expected instanceof IPv6Type) {
returnType = IPv6Type.INSTANCE;
}
} else if (input.isDateType()) {
if (expected instanceof DateTimeType) {
@ -1205,6 +1213,16 @@ public class TypeCoercionUtils {
return Optional.of(commonType);
}
// ip type
if ((leftType.isIPv4Type() && rightType.isStringLikeType())
|| (rightType.isIPv4Type() && leftType.isStringLikeType())) {
return Optional.of(IPv4Type.INSTANCE);
}
if ((leftType.isIPv6Type() && rightType.isStringLikeType())
|| (rightType.isIPv6Type() && leftType.isStringLikeType())) {
return Optional.of(IPv6Type.INSTANCE);
}
return Optional.of(DoubleType.INSTANCE);
}