From 5d8c62c364aa6f87a0505836b3283a9b05fdfcba Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Wed, 25 Jun 2025 15:13:30 +0800 Subject: [PATCH 01/15] ip_compare operator added Signed-off-by: Xinyu Hao --- .../utils/UserDefinedFunctionUtils.java | 2 +- .../function/PPLBuiltinOperators.java | 10 ++- .../expression/function/PPLFuncImpTable.java | 9 ++ .../function/udf/ip/EqualsIpFunction.java | 87 +++++++++++++++++++ .../function/udf/ip/GreaterIpFunction.java | 87 +++++++++++++++++++ .../function/udf/ip/GteIpFunction.java | 87 +++++++++++++++++++ .../function/udf/ip/LessIpFunction.java | 87 +++++++++++++++++++ .../function/udf/ip/LteIpFunction.java | 87 +++++++++++++++++++ .../function/udf/ip/NotEqualsIpFunction.java | 87 +++++++++++++++++++ 9 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java diff --git a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java index 5baf9983450..827929d8cf7 100644 --- a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java +++ b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java @@ -115,7 +115,7 @@ public static SqlTypeName convertRelDataTypeToSqlTypeName(RelDataType type) { case EXPR_DATE -> SqlTypeName.DATE; case EXPR_TIME -> SqlTypeName.TIME; case EXPR_TIMESTAMP -> SqlTypeName.TIMESTAMP; - case EXPR_IP -> SqlTypeName.VARCHAR; + case EXPR_IP -> SqlTypeName.OTHER; // for ip comparison, we use SqlTypeName.OTHER case EXPR_BINARY -> SqlTypeName.VARBINARY; default -> type.getSqlTypeName(); }; diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java index 1b3a2b7dba8..9aa00c586fa 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java @@ -70,7 +70,7 @@ import org.opensearch.sql.expression.function.udf.datetime.WeekFunction; import org.opensearch.sql.expression.function.udf.datetime.WeekdayFunction; import org.opensearch.sql.expression.function.udf.datetime.YearweekFunction; -import org.opensearch.sql.expression.function.udf.ip.CidrMatchFunction; +import org.opensearch.sql.expression.function.udf.ip.*; import org.opensearch.sql.expression.function.udf.math.CRC32Function; import org.opensearch.sql.expression.function.udf.math.ConvFunction; import org.opensearch.sql.expression.function.udf.math.DivideFunction; @@ -102,6 +102,14 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable { public static final SqlOperator SHA2 = CryptographicFunction.sha2().toUDF("SHA2"); public static final SqlOperator CIDRMATCH = new CidrMatchFunction().toUDF("CIDRMATCH"); + // IP comparing functions + public static final SqlOperator NOT_EQUALS_IP = new NotEqualsIpFunction().toUDF("NOT_EQUALS_IP"); + public static final SqlOperator EQUALS_IP = new EqualsIpFunction().toUDF("EQUALS_IP"); + public static final SqlOperator GREATER_IP = new GreaterIpFunction().toUDF("GREATER_IP"); + public static final SqlOperator GTE_IP = new GteIpFunction().toUDF("GTE_IP"); + public static final SqlOperator LESS_IP = new LessIpFunction().toUDF("LESS_IP"); + public static final SqlOperator LTE_IP = new LteIpFunction().toUDF("LTE_IP"); + // Condition function public static final SqlOperator EARLIEST = new EarliestFunction().toUDF("EARLIEST"); public static final SqlOperator LATEST = new LatestFunction().toUDF("LATEST"); diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index 0019e6819d6..b57b6d76bf2 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -614,6 +614,14 @@ public PPLTypeChecker getTypeChecker() { } void populate() { + // register operators for IP comparing + registerOperator(NOTEQUAL, PPLBuiltinOperators.NOT_EQUALS_IP); + registerOperator(EQUAL, PPLBuiltinOperators.EQUALS_IP); + + registerOperator(GTE, PPLBuiltinOperators.GTE_IP); + registerOperator(LESS, PPLBuiltinOperators.LESS_IP); + registerOperator(LTE, PPLBuiltinOperators.LTE_IP); + // Register std operator registerOperator(AND, SqlStdOperatorTable.AND); registerOperator(OR, SqlStdOperatorTable.OR); @@ -621,6 +629,7 @@ void populate() { registerOperator(NOTEQUAL, SqlStdOperatorTable.NOT_EQUALS); registerOperator(EQUAL, SqlStdOperatorTable.EQUALS); registerOperator(GREATER, SqlStdOperatorTable.GREATER_THAN); + registerOperator(GREATER, PPLBuiltinOperators.GREATER_IP); registerOperator(GTE, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL); registerOperator(LESS, SqlStdOperatorTable.LESS_THAN); registerOperator(LTE, SqlStdOperatorTable.LESS_THAN_OR_EQUAL); diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java new file mode 100644 index 00000000000..d15e7f805f2 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.*; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code Equals(ip1, ip2)} checks if two IP addresses are equal. + * + *

Signature: + * + *

+ */ +public class EqualsIpFunction extends ImplementorUDF { + public EqualsIpFunction() { + super(new EqualsImplementor(), NullPolicy.ANY); + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + } + + public static class EqualsImplementor implements NotNullImplementor { + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call(EqualsImplementor.class, "Equals", translatedOperands); + } + + public static boolean Equals(String ip1, String ip2) { + try { + IPAddress ipAddress1 = IPUtils.toAddress(ip1); + IPAddress ipAddress2 = IPUtils.toAddress(ip2); + return IPUtils.compare(ipAddress1, ipAddress2) == 0; + } catch (SemanticCheckException e) { + return false; + } + } + + public static boolean Equals(String ip1, ExprIpValue ip2) { + String ipAddress2 = ip2.value(); + return Equals(ip1, ipAddress2); + } + + public static boolean Equals(ExprIpValue ip1, String ip2) { + String ipAddress1 = ip1.value(); + return Equals(ipAddress1, ip2); + } + + public static boolean Equals(ExprIpValue ip1, ExprIpValue ip2) { + String ipAddress1 = ip1.value(); + String ipAddress2 = ip2.value(); + return Equals(ipAddress1, ipAddress2); + } + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java new file mode 100644 index 00000000000..87daa9f2d0d --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.*; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code Greater(ip1, ip2)} checks if ip1 is greater than ip2. + * + *

Signature: + * + *

    + *
  • (STRING, STRING) -> BOOLEAN + *
  • (IP, STRING) -> BOOLEAN + *
  • (STRING, IP) -> BOOLEAN + *
  • (IP, IP) -> BOOLEAN + *
+ */ +public class GreaterIpFunction extends ImplementorUDF { + public GreaterIpFunction() { + super(new GreaterImplementor(), NullPolicy.ANY); + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + } + + public static class GreaterImplementor implements NotNullImplementor { + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call(GreaterImplementor.class, "Greater", translatedOperands); + } + + public static boolean Greater(String ip1, String ip2) { + try { + IPAddress ipAddress1 = IPUtils.toAddress(ip1); + IPAddress ipAddress2 = IPUtils.toAddress(ip2); + return IPUtils.compare(ipAddress1, ipAddress2) > 0; + } catch (SemanticCheckException e) { + return false; + } + } + + public static boolean Greater(String ip1, ExprIpValue ip2) { + String ipAddress2 = ip2.value(); + return Greater(ip1, ipAddress2); + } + + public static boolean Greater(ExprIpValue ip1, String ip2) { + String ipAddress1 = ip1.value(); + return Greater(ipAddress1, ip2); + } + + public static boolean Greater(ExprIpValue ip1, ExprIpValue ip2) { + String ipAddress1 = ip1.value(); + String ipAddress2 = ip2.value(); + return Greater(ipAddress1, ipAddress2); + } + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java new file mode 100644 index 00000000000..ab76a80aa64 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.*; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code Gte(ip1, ip2)} checks if ip1 is greater than or equals ip2. + * + *

Signature: + * + *

    + *
  • (STRING, STRING) -> BOOLEAN + *
  • (IP, STRING) -> BOOLEAN + *
  • (STRING, IP) -> BOOLEAN + *
  • (IP, IP) -> BOOLEAN + *
+ */ +public class GteIpFunction extends ImplementorUDF { + public GteIpFunction() { + super(new GteImplementor(), NullPolicy.ANY); + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + } + + public static class GteImplementor implements NotNullImplementor { + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call(GteImplementor.class, "Gte", translatedOperands); + } + + public static boolean Gte(String ip1, String ip2) { + try { + IPAddress ipAddress1 = IPUtils.toAddress(ip1); + IPAddress ipAddress2 = IPUtils.toAddress(ip2); + return IPUtils.compare(ipAddress1, ipAddress2) >= 0; + } catch (SemanticCheckException e) { + return false; + } + } + + public static boolean Gte(String ip1, ExprIpValue ip2) { + String ipAddress2 = ip2.value(); + return Gte(ip1, ipAddress2); + } + + public static boolean Gte(ExprIpValue ip1, String ip2) { + String ipAddress1 = ip1.value(); + return Gte(ipAddress1, ip2); + } + + public static boolean Gte(ExprIpValue ip1, ExprIpValue ip2) { + String ipAddress1 = ip1.value(); + String ipAddress2 = ip2.value(); + return Gte(ipAddress1, ipAddress2); + } + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java new file mode 100644 index 00000000000..5bd9fd1bd1d --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.*; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code Less(ip1, ip2)} checks if ip1 is less than ip2. + * + *

Signature: + * + *

    + *
  • (STRING, STRING) -> BOOLEAN + *
  • (IP, STRING) -> BOOLEAN + *
  • (STRING, IP) -> BOOLEAN + *
  • (IP, IP) -> BOOLEAN + *
+ */ +public class LessIpFunction extends ImplementorUDF { + public LessIpFunction() { + super(new LessImplementor(), NullPolicy.ANY); + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + } + + public static class LessImplementor implements NotNullImplementor { + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call(LessImplementor.class, "Less", translatedOperands); + } + + public static boolean Less(String ip1, String ip2) { + try { + IPAddress ipAddress1 = IPUtils.toAddress(ip1); + IPAddress ipAddress2 = IPUtils.toAddress(ip2); + return IPUtils.compare(ipAddress1, ipAddress2) <= 0; + } catch (SemanticCheckException e) { + return false; + } + } + + public static boolean Less(String ip1, ExprIpValue ip2) { + String ipAddress2 = ip2.value(); + return Less(ip1, ipAddress2); + } + + public static boolean Less(ExprIpValue ip1, String ip2) { + String ipAddress1 = ip1.value(); + return Less(ipAddress1, ip2); + } + + public static boolean Less(ExprIpValue ip1, ExprIpValue ip2) { + String ipAddress1 = ip1.value(); + String ipAddress2 = ip2.value(); + return Less(ipAddress1, ipAddress2); + } + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java new file mode 100644 index 00000000000..89962d38672 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.*; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code Lte(ip1, ip2)} checks if ip1 is less than or equals ip2. + * + *

Signature: + * + *

    + *
  • (STRING, STRING) -> BOOLEAN + *
  • (IP, STRING) -> BOOLEAN + *
  • (STRING, IP) -> BOOLEAN + *
  • (IP, IP) -> BOOLEAN + *
+ */ +public class LteIpFunction extends ImplementorUDF { + public LteIpFunction() { + super(new LteImplementor(), NullPolicy.ANY); + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + } + + public static class LteImplementor implements NotNullImplementor { + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call(LteImplementor.class, "Lte", translatedOperands); + } + + public static boolean Lte(String ip1, String ip2) { + try { + IPAddress ipAddress1 = IPUtils.toAddress(ip1); + IPAddress ipAddress2 = IPUtils.toAddress(ip2); + return IPUtils.compare(ipAddress1, ipAddress2) <= 0; + } catch (SemanticCheckException e) { + return false; + } + } + + public static boolean Lte(String ip1, ExprIpValue ip2) { + String ipAddress2 = ip2.value(); + return Lte(ip1, ipAddress2); + } + + public static boolean Lte(ExprIpValue ip1, String ip2) { + String ipAddress1 = ip1.value(); + return Lte(ipAddress1, ip2); + } + + public static boolean Lte(ExprIpValue ip1, ExprIpValue ip2) { + String ipAddress1 = ip1.value(); + String ipAddress2 = ip2.value(); + return Lte(ipAddress1, ipAddress2); + } + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java new file mode 100644 index 00000000000..d6abb734229 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java @@ -0,0 +1,87 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.*; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code notEquals(ip1, ip2)} checks if two IP addresses are not equal. + * + *

Signature: + * + *

    + *
  • (STRING, STRING) -> BOOLEAN + *
  • (IP, STRING) -> BOOLEAN + *
  • (STRING, IP) -> BOOLEAN + *
  • (IP, IP) -> BOOLEAN + *
+ */ +public class NotEqualsIpFunction extends ImplementorUDF { + public NotEqualsIpFunction() { + super(new NotEqualsImplementor(), NullPolicy.ANY); + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + } + + public static class NotEqualsImplementor implements NotNullImplementor { + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call(NotEqualsImplementor.class, "notEquals", translatedOperands); + } + + public static boolean notEquals(String ip1, String ip2) { + try { + IPAddress ipAddress1 = IPUtils.toAddress(ip1); + IPAddress ipAddress2 = IPUtils.toAddress(ip2); + return IPUtils.compare(ipAddress1, ipAddress2) != 0; + } catch (SemanticCheckException e) { + return true; + } + } + + public static boolean notEquals(String ip1, ExprIpValue ip2) { + String ipAddress2 = ip2.value(); + return notEquals(ip1, ipAddress2); + } + + public static boolean notEquals(ExprIpValue ip1, String ip2) { + String ipAddress1 = ip1.value(); + return notEquals(ipAddress1, ip2); + } + + public static boolean notEquals(ExprIpValue ip1, ExprIpValue ip2) { + String ipAddress1 = ip1.value(); + String ipAddress2 = ip2.value(); + return notEquals(ipAddress1, ipAddress2); + } + } +} From 3008d759d770b1817957499da0761de06d8d1e13 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Wed, 25 Jun 2025 17:05:23 +0800 Subject: [PATCH 02/15] only type checker issue left Signed-off-by: Xinyu Hao --- .../sql/calcite/utils/UserDefinedFunctionUtils.java | 2 +- .../sql/expression/function/PPLFuncImpTable.java | 3 +-- .../function/udf/ip/EqualsIpFunction.java | 13 ++++++++----- .../function/udf/ip/GreaterIpFunction.java | 13 ++++++++----- .../expression/function/udf/ip/GteIpFunction.java | 13 ++++++++----- .../expression/function/udf/ip/LessIpFunction.java | 13 ++++++++----- .../expression/function/udf/ip/LteIpFunction.java | 13 ++++++++----- .../function/udf/ip/NotEqualsIpFunction.java | 13 ++++++++----- 8 files changed, 50 insertions(+), 33 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java index 827929d8cf7..5baf9983450 100644 --- a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java +++ b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java @@ -115,7 +115,7 @@ public static SqlTypeName convertRelDataTypeToSqlTypeName(RelDataType type) { case EXPR_DATE -> SqlTypeName.DATE; case EXPR_TIME -> SqlTypeName.TIME; case EXPR_TIMESTAMP -> SqlTypeName.TIMESTAMP; - case EXPR_IP -> SqlTypeName.OTHER; // for ip comparison, we use SqlTypeName.OTHER + case EXPR_IP -> SqlTypeName.VARCHAR; case EXPR_BINARY -> SqlTypeName.VARBINARY; default -> type.getSqlTypeName(); }; diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index b57b6d76bf2..981f3be585f 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -617,7 +617,7 @@ void populate() { // register operators for IP comparing registerOperator(NOTEQUAL, PPLBuiltinOperators.NOT_EQUALS_IP); registerOperator(EQUAL, PPLBuiltinOperators.EQUALS_IP); - + registerOperator(GREATER, PPLBuiltinOperators.GREATER_IP); registerOperator(GTE, PPLBuiltinOperators.GTE_IP); registerOperator(LESS, PPLBuiltinOperators.LESS_IP); registerOperator(LTE, PPLBuiltinOperators.LTE_IP); @@ -629,7 +629,6 @@ void populate() { registerOperator(NOTEQUAL, SqlStdOperatorTable.NOT_EQUALS); registerOperator(EQUAL, SqlStdOperatorTable.EQUALS); registerOperator(GREATER, SqlStdOperatorTable.GREATER_THAN); - registerOperator(GREATER, PPLBuiltinOperators.GREATER_IP); registerOperator(GTE, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL); registerOperator(LESS, SqlStdOperatorTable.LESS_THAN); registerOperator(LTE, SqlStdOperatorTable.LESS_THAN_OR_EQUAL); diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java index d15e7f805f2..02f0ddded5c 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java @@ -43,12 +43,15 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override + // public UDFOperandMetadata getOperandMetadata() { + // return UDFOperandMetadata.wrap( + // (CompositeOperandTypeChecker) + // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); } public static class EqualsImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java index 87daa9f2d0d..6b946629f64 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java @@ -43,12 +43,15 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override + // public UDFOperandMetadata getOperandMetadata() { + // return UDFOperandMetadata.wrap( + // (CompositeOperandTypeChecker) + // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); } public static class GreaterImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java index ab76a80aa64..5dcb6072324 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java @@ -43,12 +43,15 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override + // public UDFOperandMetadata getOperandMetadata() { + // return UDFOperandMetadata.wrap( + // (CompositeOperandTypeChecker) + // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); } public static class GteImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java index 5bd9fd1bd1d..0f772c95e6a 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java @@ -43,12 +43,15 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override + // public UDFOperandMetadata getOperandMetadata() { + // return UDFOperandMetadata.wrap( + // (CompositeOperandTypeChecker) + // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); } public static class LessImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java index 89962d38672..9e1c35a93e0 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java @@ -43,12 +43,15 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override + // public UDFOperandMetadata getOperandMetadata() { + // return UDFOperandMetadata.wrap( + // (CompositeOperandTypeChecker) + // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); } public static class LteImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java index d6abb734229..9a1331c1d81 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java @@ -43,12 +43,15 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override + // public UDFOperandMetadata getOperandMetadata() { + // return UDFOperandMetadata.wrap( + // (CompositeOperandTypeChecker) + // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.ANY) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.ANY))); + return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); } public static class NotEqualsImplementor implements NotNullImplementor { From 851f40e9478fd588a9e360d4dff6915ccafba13a Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Wed, 25 Jun 2025 17:57:19 +0800 Subject: [PATCH 03/15] fix by modifying ip.sqlTypeName from OTHER to NULL in type checker Signed-off-by: Xinyu Hao --- .../sql/calcite/utils/UserDefinedFunctionUtils.java | 5 ++++- .../function/udf/ip/CidrMatchFunction.java | 11 ++++++----- .../function/udf/ip/EqualsIpFunction.java | 13 +++++-------- .../function/udf/ip/GreaterIpFunction.java | 13 +++++-------- .../expression/function/udf/ip/GteIpFunction.java | 13 +++++-------- .../expression/function/udf/ip/LessIpFunction.java | 13 +++++-------- .../expression/function/udf/ip/LteIpFunction.java | 13 +++++-------- .../function/udf/ip/NotEqualsIpFunction.java | 13 +++++-------- .../sql/opensearch/functions/GeoIpFunction.java | 5 +---- 9 files changed, 41 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java index 5baf9983450..f220c2265b2 100644 --- a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java +++ b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java @@ -115,7 +115,10 @@ public static SqlTypeName convertRelDataTypeToSqlTypeName(RelDataType type) { case EXPR_DATE -> SqlTypeName.DATE; case EXPR_TIME -> SqlTypeName.TIME; case EXPR_TIMESTAMP -> SqlTypeName.TIMESTAMP; - case EXPR_IP -> SqlTypeName.VARCHAR; + // EXPR_IP is mapped to SqlTypeName.NULL since there is no + // corresponding SqlTypeName in Calcite. This is a workaround to allow + // type checking for IP types in UDFs. + case EXPR_IP -> SqlTypeName.NULL; case EXPR_BINARY -> SqlTypeName.VARBINARY; default -> type.getSqlTypeName(); }; diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java index d7879d881d4..9aa00257f8c 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java @@ -12,9 +12,7 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.Expressions; import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.OperandTypes; -import org.apache.calcite.sql.type.ReturnTypes; -import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.apache.calcite.sql.type.*; import org.opensearch.sql.data.model.ExprIpValue; import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.data.model.ExprValueUtils; @@ -44,9 +42,12 @@ public SqlReturnTypeInference getReturnTypeInference() { @Override public UDFOperandMetadata getOperandMetadata() { - // EXPR_IP is mapped to SqlTypeFamily.VARCHAR in + // EXPR_IP is mapped to SqlTypeFamily.NULL in // UserDefinedFunctionUtils.convertRelDataTypeToSqlTypeName - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING))); } public static class CidrMatchImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java index 02f0ddded5c..9ec887302da 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java @@ -43,15 +43,12 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override - // public UDFOperandMetadata getOperandMetadata() { - // return UDFOperandMetadata.wrap( - // (CompositeOperandTypeChecker) - // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); } public static class EqualsImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java index 6b946629f64..df5c4daf8be 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java @@ -43,15 +43,12 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override - // public UDFOperandMetadata getOperandMetadata() { - // return UDFOperandMetadata.wrap( - // (CompositeOperandTypeChecker) - // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); } public static class GreaterImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java index 5dcb6072324..690d34d2608 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java @@ -43,15 +43,12 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override - // public UDFOperandMetadata getOperandMetadata() { - // return UDFOperandMetadata.wrap( - // (CompositeOperandTypeChecker) - // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); } public static class GteImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java index 0f772c95e6a..1107b6e1e79 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java @@ -43,15 +43,12 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override - // public UDFOperandMetadata getOperandMetadata() { - // return UDFOperandMetadata.wrap( - // (CompositeOperandTypeChecker) - // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); } public static class LessImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java index 9e1c35a93e0..d5320d90d2b 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java @@ -43,15 +43,12 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override - // public UDFOperandMetadata getOperandMetadata() { - // return UDFOperandMetadata.wrap( - // (CompositeOperandTypeChecker) - // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); } public static class LteImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java index 9a1331c1d81..d2ef0156f98 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java @@ -43,15 +43,12 @@ public SqlReturnTypeInference getReturnTypeInference() { } @Override - // public UDFOperandMetadata getOperandMetadata() { - // return UDFOperandMetadata.wrap( - // (CompositeOperandTypeChecker) - // OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - // .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - // } public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap(OperandTypes.STRING_STRING); + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); } public static class NotEqualsImplementor implements NotNullImplementor { diff --git a/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java b/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java index ae7eb1a3a4e..3e2139cea7d 100644 --- a/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java +++ b/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java @@ -16,10 +16,7 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.CompositeOperandTypeChecker; -import org.apache.calcite.sql.type.OperandTypes; -import org.apache.calcite.sql.type.SqlReturnTypeInference; -import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.type.*; import org.opensearch.geospatial.action.IpEnrichmentActionClient; import org.opensearch.sql.common.utils.StringUtils; import org.opensearch.sql.data.model.ExprStringValue; From e4f619e66cc1c84d66c916f6dfb7a36c87fbf714 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Thu, 26 Jun 2025 10:53:15 +0800 Subject: [PATCH 04/15] fix less Signed-off-by: Xinyu Hao --- .../sql/expression/function/udf/ip/LessIpFunction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java index 1107b6e1e79..e87c44b5260 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java @@ -62,7 +62,7 @@ public static boolean Less(String ip1, String ip2) { try { IPAddress ipAddress1 = IPUtils.toAddress(ip1); IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) <= 0; + return IPUtils.compare(ipAddress1, ipAddress2) < 0; } catch (SemanticCheckException e) { return false; } From 73bec7b123f40791b6245b9f00f8ce5158ac0ba8 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Thu, 26 Jun 2025 14:17:20 +0800 Subject: [PATCH 05/15] modify the CalcitePPLFunctionTypeTest text Signed-off-by: Xinyu Hao --- .../opensearch/sql/expression/function/PPLTypeChecker.java | 4 ++++ .../sql/ppl/calcite/CalcitePPLFunctionTypeTest.java | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java index d3443ab850e..17e9e290add 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java @@ -400,6 +400,10 @@ private static List getExprTypes(SqlTypeFamily family) { OpenSearchTypeFactory.TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER)); case ANY, IGNORE -> List.of( OpenSearchTypeFactory.TYPE_FACTORY.createSqlType(SqlTypeName.ANY)); + // We borrow SqlTypeFamily.NULL to represent EXPR_IP. This is a workaround + // since there is no corresponding IP type family in Calcite. + case NULL -> List.of( + OpenSearchTypeFactory.TYPE_FACTORY.createUDT(OpenSearchTypeFactory.ExprUDT.EXPR_IP)); default -> { RelDataType type = family.getDefaultConcreteType(OpenSearchTypeFactory.TYPE_FACTORY); if (type == null) { diff --git a/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java b/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java index 55f56350480..20447be761a 100644 --- a/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java +++ b/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java @@ -49,7 +49,9 @@ public void testComparisonWithDifferentType() { String ppl = "source=EMP | where ENAME < 6 | fields ENAME"; Throwable t = Assert.assertThrows(ExpressionEvaluationException.class, () -> getRelNode(ppl)); verifyErrorMessageContains( - t, "LESS function expects {[COMPARABLE_TYPE,COMPARABLE_TYPE]}, but got [STRING,INTEGER]"); + t, + "LESS function expects {[STRING,IP],[IP,STRING],[IP,IP],[COMPARABLE_TYPE,COMPARABLE_TYPE]}," + + " but got [STRING,INTEGER]"); } @Test From 2f9c5b4f83a42d26ec0a6d86c6ba5b36a67b0d69 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Fri, 27 Jun 2025 10:58:58 +0800 Subject: [PATCH 06/15] allow CalciteIPComparisonIT in CalciteNoPushdownIT Signed-off-by: Xinyu Hao --- .../opensearch/sql/calcite/pushdown/CalciteNoPushdownIT.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/pushdown/CalciteNoPushdownIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/pushdown/CalciteNoPushdownIT.java index 14ae021e66a..6dbc078acc8 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/pushdown/CalciteNoPushdownIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/pushdown/CalciteNoPushdownIT.java @@ -40,9 +40,7 @@ CalciteGeoPointFormatsIT.class, CalciteHeadCommandIT.class, CalciteInformationSchemaCommandIT.class, - // TODO: Enable after implementing comparison for IP addresses with Calcite - // https://github.com/opensearch-project/sql/issues/3776 - // CalciteIPComparisonIT.class, + CalciteIPComparisonIT.class, CalciteIPFunctionsIT.class, CalciteJsonFunctionsIT.class, CalciteLegacyAPICompatibilityIT.class, From 40d80a4385d5070901cac46fc631df841068ff3e Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Fri, 27 Jun 2025 11:26:51 +0800 Subject: [PATCH 07/15] Modify the signature description in udf Signed-off-by: Xinyu Hao --- .../sql/expression/function/udf/ip/EqualsIpFunction.java | 1 - .../sql/expression/function/udf/ip/GreaterIpFunction.java | 1 - .../opensearch/sql/expression/function/udf/ip/GteIpFunction.java | 1 - .../sql/expression/function/udf/ip/LessIpFunction.java | 1 - .../opensearch/sql/expression/function/udf/ip/LteIpFunction.java | 1 - .../sql/expression/function/udf/ip/NotEqualsIpFunction.java | 1 - 6 files changed, 6 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java index 9ec887302da..22bc4d6f74a 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java @@ -26,7 +26,6 @@ *

Signature: * *

    - *
  • (STRING, STRING) -> BOOLEAN *
  • (IP, STRING) -> BOOLEAN *
  • (STRING, IP) -> BOOLEAN *
  • (IP, IP) -> BOOLEAN diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java index df5c4daf8be..1d9a866e5a9 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java @@ -26,7 +26,6 @@ *

    Signature: * *

      - *
    • (STRING, STRING) -> BOOLEAN *
    • (IP, STRING) -> BOOLEAN *
    • (STRING, IP) -> BOOLEAN *
    • (IP, IP) -> BOOLEAN diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java index 690d34d2608..adeaeb887a5 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java @@ -26,7 +26,6 @@ *

      Signature: * *

        - *
      • (STRING, STRING) -> BOOLEAN *
      • (IP, STRING) -> BOOLEAN *
      • (STRING, IP) -> BOOLEAN *
      • (IP, IP) -> BOOLEAN diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java index e87c44b5260..f3c1db4d83b 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java @@ -26,7 +26,6 @@ *

        Signature: * *

          - *
        • (STRING, STRING) -> BOOLEAN *
        • (IP, STRING) -> BOOLEAN *
        • (STRING, IP) -> BOOLEAN *
        • (IP, IP) -> BOOLEAN diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java index d5320d90d2b..42c3eb928a9 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java @@ -26,7 +26,6 @@ *

          Signature: * *

            - *
          • (STRING, STRING) -> BOOLEAN *
          • (IP, STRING) -> BOOLEAN *
          • (STRING, IP) -> BOOLEAN *
          • (IP, IP) -> BOOLEAN diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java index d2ef0156f98..db7dc3e37e8 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java @@ -26,7 +26,6 @@ *

            Signature: * *

              - *
            • (STRING, STRING) -> BOOLEAN *
            • (IP, STRING) -> BOOLEAN *
            • (STRING, IP) -> BOOLEAN *
            • (IP, IP) -> BOOLEAN From a4916b2fb245b87e479dcd5eb6e649f2c8ad23fa Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Mon, 30 Jun 2025 11:35:22 +0800 Subject: [PATCH 08/15] fix some typing errors Signed-off-by: Xinyu Hao --- .../function/PPLBuiltinOperators.java | 8 +++++++- .../function/udf/ip/CidrMatchFunction.java | 6 +++++- .../function/udf/ip/EqualsIpFunction.java | 18 +++++++++--------- .../function/udf/ip/GreaterIpFunction.java | 18 +++++++++--------- .../function/udf/ip/GteIpFunction.java | 18 +++++++++--------- .../function/udf/ip/LessIpFunction.java | 18 +++++++++--------- .../function/udf/ip/LteIpFunction.java | 18 +++++++++--------- .../opensearch/functions/GeoIpFunction.java | 5 ++++- 8 files changed, 61 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java index 9aa00c586fa..45a8e84610d 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java @@ -70,7 +70,13 @@ import org.opensearch.sql.expression.function.udf.datetime.WeekFunction; import org.opensearch.sql.expression.function.udf.datetime.WeekdayFunction; import org.opensearch.sql.expression.function.udf.datetime.YearweekFunction; -import org.opensearch.sql.expression.function.udf.ip.*; +import org.opensearch.sql.expression.function.udf.ip.CidrMatchFunction; +import org.opensearch.sql.expression.function.udf.ip.EqualsIpFunction; +import org.opensearch.sql.expression.function.udf.ip.GreaterIpFunction; +import org.opensearch.sql.expression.function.udf.ip.GteIpFunction; +import org.opensearch.sql.expression.function.udf.ip.LessIpFunction; +import org.opensearch.sql.expression.function.udf.ip.LteIpFunction; +import org.opensearch.sql.expression.function.udf.ip.NotEqualsIpFunction; import org.opensearch.sql.expression.function.udf.math.CRC32Function; import org.opensearch.sql.expression.function.udf.math.ConvFunction; import org.opensearch.sql.expression.function.udf.math.DivideFunction; diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java index 9aa00257f8c..4c8e532cbb0 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java @@ -12,7 +12,11 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.Expressions; import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; +import org.apache.calcite.sql.type.CompositeOperandTypeChecker; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.apache.calcite.sql.type.SqlTypeFamily; import org.opensearch.sql.data.model.ExprIpValue; import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.data.model.ExprValueUtils; diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java index 22bc4d6f74a..b0b950fb06a 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java @@ -21,7 +21,7 @@ import org.opensearch.sql.utils.IPUtils; /** - * {@code Equals(ip1, ip2)} checks if two IP addresses are equal. + * {@code equals(ip1, ip2)} checks if two IP addresses are equal. * *

              Signature: * @@ -54,10 +54,10 @@ public static class EqualsImplementor implements NotNullImplementor { @Override public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(EqualsImplementor.class, "Equals", translatedOperands); + return Expressions.call(EqualsImplementor.class, "equals", translatedOperands); } - public static boolean Equals(String ip1, String ip2) { + public static boolean equals(String ip1, String ip2) { try { IPAddress ipAddress1 = IPUtils.toAddress(ip1); IPAddress ipAddress2 = IPUtils.toAddress(ip2); @@ -67,20 +67,20 @@ public static boolean Equals(String ip1, String ip2) { } } - public static boolean Equals(String ip1, ExprIpValue ip2) { + public static boolean equals(String ip1, ExprIpValue ip2) { String ipAddress2 = ip2.value(); - return Equals(ip1, ipAddress2); + return equals(ip1, ipAddress2); } - public static boolean Equals(ExprIpValue ip1, String ip2) { + public static boolean equals(ExprIpValue ip1, String ip2) { String ipAddress1 = ip1.value(); - return Equals(ipAddress1, ip2); + return equals(ipAddress1, ip2); } - public static boolean Equals(ExprIpValue ip1, ExprIpValue ip2) { + public static boolean equals(ExprIpValue ip1, ExprIpValue ip2) { String ipAddress1 = ip1.value(); String ipAddress2 = ip2.value(); - return Equals(ipAddress1, ipAddress2); + return equals(ipAddress1, ipAddress2); } } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java index 1d9a866e5a9..715732b2225 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java @@ -21,7 +21,7 @@ import org.opensearch.sql.utils.IPUtils; /** - * {@code Greater(ip1, ip2)} checks if ip1 is greater than ip2. + * {@code greater(ip1, ip2)} checks if ip1 is greater than ip2. * *

              Signature: * @@ -54,10 +54,10 @@ public static class GreaterImplementor implements NotNullImplementor { @Override public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(GreaterImplementor.class, "Greater", translatedOperands); + return Expressions.call(GreaterImplementor.class, "greater", translatedOperands); } - public static boolean Greater(String ip1, String ip2) { + public static boolean greater(String ip1, String ip2) { try { IPAddress ipAddress1 = IPUtils.toAddress(ip1); IPAddress ipAddress2 = IPUtils.toAddress(ip2); @@ -67,20 +67,20 @@ public static boolean Greater(String ip1, String ip2) { } } - public static boolean Greater(String ip1, ExprIpValue ip2) { + public static boolean greater(String ip1, ExprIpValue ip2) { String ipAddress2 = ip2.value(); - return Greater(ip1, ipAddress2); + return greater(ip1, ipAddress2); } - public static boolean Greater(ExprIpValue ip1, String ip2) { + public static boolean greater(ExprIpValue ip1, String ip2) { String ipAddress1 = ip1.value(); - return Greater(ipAddress1, ip2); + return greater(ipAddress1, ip2); } - public static boolean Greater(ExprIpValue ip1, ExprIpValue ip2) { + public static boolean greater(ExprIpValue ip1, ExprIpValue ip2) { String ipAddress1 = ip1.value(); String ipAddress2 = ip2.value(); - return Greater(ipAddress1, ipAddress2); + return greater(ipAddress1, ipAddress2); } } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java index adeaeb887a5..769fac4a986 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java @@ -21,7 +21,7 @@ import org.opensearch.sql.utils.IPUtils; /** - * {@code Gte(ip1, ip2)} checks if ip1 is greater than or equals ip2. + * {@code gte(ip1, ip2)} checks if ip1 is greater than or equals ip2. * *

              Signature: * @@ -54,10 +54,10 @@ public static class GteImplementor implements NotNullImplementor { @Override public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(GteImplementor.class, "Gte", translatedOperands); + return Expressions.call(GteImplementor.class, "gte", translatedOperands); } - public static boolean Gte(String ip1, String ip2) { + public static boolean gte(String ip1, String ip2) { try { IPAddress ipAddress1 = IPUtils.toAddress(ip1); IPAddress ipAddress2 = IPUtils.toAddress(ip2); @@ -67,20 +67,20 @@ public static boolean Gte(String ip1, String ip2) { } } - public static boolean Gte(String ip1, ExprIpValue ip2) { + public static boolean gte(String ip1, ExprIpValue ip2) { String ipAddress2 = ip2.value(); - return Gte(ip1, ipAddress2); + return gte(ip1, ipAddress2); } - public static boolean Gte(ExprIpValue ip1, String ip2) { + public static boolean gte(ExprIpValue ip1, String ip2) { String ipAddress1 = ip1.value(); - return Gte(ipAddress1, ip2); + return gte(ipAddress1, ip2); } - public static boolean Gte(ExprIpValue ip1, ExprIpValue ip2) { + public static boolean gte(ExprIpValue ip1, ExprIpValue ip2) { String ipAddress1 = ip1.value(); String ipAddress2 = ip2.value(); - return Gte(ipAddress1, ipAddress2); + return gte(ipAddress1, ipAddress2); } } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java index f3c1db4d83b..e554a241173 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java @@ -21,7 +21,7 @@ import org.opensearch.sql.utils.IPUtils; /** - * {@code Less(ip1, ip2)} checks if ip1 is less than ip2. + * {@code less(ip1, ip2)} checks if ip1 is less than ip2. * *

              Signature: * @@ -54,10 +54,10 @@ public static class LessImplementor implements NotNullImplementor { @Override public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(LessImplementor.class, "Less", translatedOperands); + return Expressions.call(LessImplementor.class, "less", translatedOperands); } - public static boolean Less(String ip1, String ip2) { + public static boolean less(String ip1, String ip2) { try { IPAddress ipAddress1 = IPUtils.toAddress(ip1); IPAddress ipAddress2 = IPUtils.toAddress(ip2); @@ -67,20 +67,20 @@ public static boolean Less(String ip1, String ip2) { } } - public static boolean Less(String ip1, ExprIpValue ip2) { + public static boolean less(String ip1, ExprIpValue ip2) { String ipAddress2 = ip2.value(); - return Less(ip1, ipAddress2); + return less(ip1, ipAddress2); } - public static boolean Less(ExprIpValue ip1, String ip2) { + public static boolean less(ExprIpValue ip1, String ip2) { String ipAddress1 = ip1.value(); - return Less(ipAddress1, ip2); + return less(ipAddress1, ip2); } - public static boolean Less(ExprIpValue ip1, ExprIpValue ip2) { + public static boolean less(ExprIpValue ip1, ExprIpValue ip2) { String ipAddress1 = ip1.value(); String ipAddress2 = ip2.value(); - return Less(ipAddress1, ipAddress2); + return less(ipAddress1, ipAddress2); } } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java index 42c3eb928a9..4b2f10391e2 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java @@ -21,7 +21,7 @@ import org.opensearch.sql.utils.IPUtils; /** - * {@code Lte(ip1, ip2)} checks if ip1 is less than or equals ip2. + * {@code lte(ip1, ip2)} checks if ip1 is less than or equals ip2. * *

              Signature: * @@ -54,10 +54,10 @@ public static class LteImplementor implements NotNullImplementor { @Override public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(LteImplementor.class, "Lte", translatedOperands); + return Expressions.call(LteImplementor.class, "lte", translatedOperands); } - public static boolean Lte(String ip1, String ip2) { + public static boolean lte(String ip1, String ip2) { try { IPAddress ipAddress1 = IPUtils.toAddress(ip1); IPAddress ipAddress2 = IPUtils.toAddress(ip2); @@ -67,20 +67,20 @@ public static boolean Lte(String ip1, String ip2) { } } - public static boolean Lte(String ip1, ExprIpValue ip2) { + public static boolean lte(String ip1, ExprIpValue ip2) { String ipAddress2 = ip2.value(); - return Lte(ip1, ipAddress2); + return lte(ip1, ipAddress2); } - public static boolean Lte(ExprIpValue ip1, String ip2) { + public static boolean lte(ExprIpValue ip1, String ip2) { String ipAddress1 = ip1.value(); - return Lte(ipAddress1, ip2); + return lte(ipAddress1, ip2); } - public static boolean Lte(ExprIpValue ip1, ExprIpValue ip2) { + public static boolean lte(ExprIpValue ip1, ExprIpValue ip2) { String ipAddress1 = ip1.value(); String ipAddress2 = ip2.value(); - return Lte(ipAddress1, ipAddress2); + return lte(ipAddress1, ipAddress2); } } } diff --git a/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java b/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java index 3e2139cea7d..ae7eb1a3a4e 100644 --- a/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java +++ b/opensearch/src/main/java/org/opensearch/sql/opensearch/functions/GeoIpFunction.java @@ -16,7 +16,10 @@ import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; +import org.apache.calcite.sql.type.CompositeOperandTypeChecker; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.apache.calcite.sql.type.SqlTypeName; import org.opensearch.geospatial.action.IpEnrichmentActionClient; import org.opensearch.sql.common.utils.StringUtils; import org.opensearch.sql.data.model.ExprStringValue; From f755494213493bf755ef19d4f981a1b77a7cca35 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Mon, 30 Jun 2025 14:18:46 +0800 Subject: [PATCH 09/15] modify the udfs for better style Signed-off-by: Xinyu Hao --- .../function/PPLBuiltinOperators.java | 22 ++-- .../function/udf/ip/CompareIpFunction.java | 114 ++++++++++++++++++ .../function/udf/ip/EqualsIpFunction.java | 86 ------------- .../function/udf/ip/GreaterIpFunction.java | 86 ------------- .../function/udf/ip/GteIpFunction.java | 86 ------------- .../udf/ip/IpComparisonOperators.java | 42 +++++++ .../function/udf/ip/LessIpFunction.java | 86 ------------- .../function/udf/ip/LteIpFunction.java | 86 ------------- .../function/udf/ip/NotEqualsIpFunction.java | 86 ------------- 9 files changed, 166 insertions(+), 528 deletions(-) create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java create mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java index 45a8e84610d..a5ddcf7bdf4 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java @@ -71,12 +71,7 @@ import org.opensearch.sql.expression.function.udf.datetime.WeekdayFunction; import org.opensearch.sql.expression.function.udf.datetime.YearweekFunction; import org.opensearch.sql.expression.function.udf.ip.CidrMatchFunction; -import org.opensearch.sql.expression.function.udf.ip.EqualsIpFunction; -import org.opensearch.sql.expression.function.udf.ip.GreaterIpFunction; -import org.opensearch.sql.expression.function.udf.ip.GteIpFunction; -import org.opensearch.sql.expression.function.udf.ip.LessIpFunction; -import org.opensearch.sql.expression.function.udf.ip.LteIpFunction; -import org.opensearch.sql.expression.function.udf.ip.NotEqualsIpFunction; +import org.opensearch.sql.expression.function.udf.ip.IpComparisonOperators; import org.opensearch.sql.expression.function.udf.math.CRC32Function; import org.opensearch.sql.expression.function.udf.math.ConvFunction; import org.opensearch.sql.expression.function.udf.math.DivideFunction; @@ -109,12 +104,15 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable { public static final SqlOperator CIDRMATCH = new CidrMatchFunction().toUDF("CIDRMATCH"); // IP comparing functions - public static final SqlOperator NOT_EQUALS_IP = new NotEqualsIpFunction().toUDF("NOT_EQUALS_IP"); - public static final SqlOperator EQUALS_IP = new EqualsIpFunction().toUDF("EQUALS_IP"); - public static final SqlOperator GREATER_IP = new GreaterIpFunction().toUDF("GREATER_IP"); - public static final SqlOperator GTE_IP = new GteIpFunction().toUDF("GTE_IP"); - public static final SqlOperator LESS_IP = new LessIpFunction().toUDF("LESS_IP"); - public static final SqlOperator LTE_IP = new LteIpFunction().toUDF("LTE_IP"); + public static final SqlOperator NOT_EQUALS_IP = + IpComparisonOperators.notEqualsIp().toUDF("NOT_EQUALS_IP"); + public static final SqlOperator EQUALS_IP = IpComparisonOperators.equalsIp().toUDF("EQUALS_IP"); + public static final SqlOperator GREATER_IP = + IpComparisonOperators.greaterThanIp().toUDF("GREATER_IP"); + public static final SqlOperator GTE_IP = + IpComparisonOperators.greaterOrEqualsIp().toUDF("GTE_IP"); + public static final SqlOperator LESS_IP = IpComparisonOperators.lessThanIp().toUDF("LESS_IP"); + public static final SqlOperator LTE_IP = IpComparisonOperators.lessOrEqualsIp().toUDF("LTE_IP"); // Condition function public static final SqlOperator EARLIEST = new EarliestFunction().toUDF("EARLIEST"); diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java new file mode 100644 index 00000000000..7fadf5ac675 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java @@ -0,0 +1,114 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +import inet.ipaddr.IPAddress; +import java.util.List; +import org.apache.calcite.adapter.enumerable.NotNullImplementor; +import org.apache.calcite.adapter.enumerable.NullPolicy; +import org.apache.calcite.adapter.enumerable.RexToLixTranslator; +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.linq4j.tree.Expressions; +import org.apache.calcite.rex.RexCall; +import org.apache.calcite.sql.type.CompositeOperandTypeChecker; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.ReturnTypes; +import org.apache.calcite.sql.type.SqlReturnTypeInference; +import org.apache.calcite.sql.type.SqlTypeFamily; +import org.opensearch.sql.data.model.ExprIpValue; +import org.opensearch.sql.expression.function.ImplementorUDF; +import org.opensearch.sql.expression.function.UDFOperandMetadata; +import org.opensearch.sql.utils.IPUtils; + +/** + * {@code compare(ip1, ip2)} compares two IP addresses using a provided op. + * + *

              Signature: + * + *

                + *
              • (IP, STRING) -> BOOLEAN + *
              • (STRING, IP) -> BOOLEAN + *
              • (IP, IP) -> BOOLEAN + *
              + */ +public class CompareIpFunction extends ImplementorUDF { + private final IpComparisonOperators.ComparisonOperator operator; + + public CompareIpFunction(IpComparisonOperators.ComparisonOperator operator) { + super(new CompareImplementor(operator), NullPolicy.ANY); + this.operator = operator; + } + + @Override + public SqlReturnTypeInference getReturnTypeInference() { + return ReturnTypes.BOOLEAN_FORCE_NULLABLE; + } + + @Override + public UDFOperandMetadata getOperandMetadata() { + return UDFOperandMetadata.wrap( + (CompositeOperandTypeChecker) + OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) + .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + } + + public static class CompareImplementor implements NotNullImplementor { + private final IpComparisonOperators.ComparisonOperator operator; + + public CompareImplementor(IpComparisonOperators.ComparisonOperator operator) { + this.operator = operator; + } + + @Override + public Expression implement( + RexToLixTranslator translator, RexCall call, List translatedOperands) { + return Expressions.call( + CompareImplementor.class, + "compare", + translatedOperands.get(0), + translatedOperands.get(1), + Expressions.constant(operator.name())); + } + + public static boolean compare(Object obj1, Object obj2, String opName) { + try { + IpComparisonOperators.ComparisonOperator op = + IpComparisonOperators.ComparisonOperator.valueOf(opName); + String ip1 = extractIpString(obj1); + String ip2 = extractIpString(obj2); + IPAddress addr1 = IPUtils.toAddress(ip1); + IPAddress addr2 = IPUtils.toAddress(ip2); + int result = IPUtils.compare(addr1, addr2); + + switch (op) { + case EQUALS: + return result == 0; + case NOT_EQUALS: + return result != 0; + case LESS: + return result < 0; + case LESS_OR_EQUAL: + return result <= 0; + case GREATER: + return result > 0; + case GREATER_OR_EQUAL: + return result >= 0; + default: + return false; + } + } catch (Exception e) { + return false; + } + } + + private static String extractIpString(Object obj) { + if (obj instanceof String) return (String) obj; + if (obj instanceof ExprIpValue) return ((ExprIpValue) obj).value(); + throw new IllegalArgumentException("Invalid IP type: " + obj); + } + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java deleted file mode 100644 index b0b950fb06a..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/EqualsIpFunction.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -import inet.ipaddr.IPAddress; -import java.util.List; -import org.apache.calcite.adapter.enumerable.NotNullImplementor; -import org.apache.calcite.adapter.enumerable.NullPolicy; -import org.apache.calcite.adapter.enumerable.RexToLixTranslator; -import org.apache.calcite.linq4j.tree.Expression; -import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; -import org.opensearch.sql.data.model.ExprIpValue; -import org.opensearch.sql.exception.SemanticCheckException; -import org.opensearch.sql.expression.function.ImplementorUDF; -import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; - -/** - * {@code equals(ip1, ip2)} checks if two IP addresses are equal. - * - *

              Signature: - * - *

                - *
              • (IP, STRING) -> BOOLEAN - *
              • (STRING, IP) -> BOOLEAN - *
              • (IP, IP) -> BOOLEAN - *
              - */ -public class EqualsIpFunction extends ImplementorUDF { - public EqualsIpFunction() { - super(new EqualsImplementor(), NullPolicy.ANY); - } - - @Override - public SqlReturnTypeInference getReturnTypeInference() { - return ReturnTypes.BOOLEAN_FORCE_NULLABLE; - } - - @Override - public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - } - - public static class EqualsImplementor implements NotNullImplementor { - @Override - public Expression implement( - RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(EqualsImplementor.class, "equals", translatedOperands); - } - - public static boolean equals(String ip1, String ip2) { - try { - IPAddress ipAddress1 = IPUtils.toAddress(ip1); - IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) == 0; - } catch (SemanticCheckException e) { - return false; - } - } - - public static boolean equals(String ip1, ExprIpValue ip2) { - String ipAddress2 = ip2.value(); - return equals(ip1, ipAddress2); - } - - public static boolean equals(ExprIpValue ip1, String ip2) { - String ipAddress1 = ip1.value(); - return equals(ipAddress1, ip2); - } - - public static boolean equals(ExprIpValue ip1, ExprIpValue ip2) { - String ipAddress1 = ip1.value(); - String ipAddress2 = ip2.value(); - return equals(ipAddress1, ipAddress2); - } - } -} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java deleted file mode 100644 index 715732b2225..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GreaterIpFunction.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -import inet.ipaddr.IPAddress; -import java.util.List; -import org.apache.calcite.adapter.enumerable.NotNullImplementor; -import org.apache.calcite.adapter.enumerable.NullPolicy; -import org.apache.calcite.adapter.enumerable.RexToLixTranslator; -import org.apache.calcite.linq4j.tree.Expression; -import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; -import org.opensearch.sql.data.model.ExprIpValue; -import org.opensearch.sql.exception.SemanticCheckException; -import org.opensearch.sql.expression.function.ImplementorUDF; -import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; - -/** - * {@code greater(ip1, ip2)} checks if ip1 is greater than ip2. - * - *

              Signature: - * - *

                - *
              • (IP, STRING) -> BOOLEAN - *
              • (STRING, IP) -> BOOLEAN - *
              • (IP, IP) -> BOOLEAN - *
              - */ -public class GreaterIpFunction extends ImplementorUDF { - public GreaterIpFunction() { - super(new GreaterImplementor(), NullPolicy.ANY); - } - - @Override - public SqlReturnTypeInference getReturnTypeInference() { - return ReturnTypes.BOOLEAN_FORCE_NULLABLE; - } - - @Override - public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - } - - public static class GreaterImplementor implements NotNullImplementor { - @Override - public Expression implement( - RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(GreaterImplementor.class, "greater", translatedOperands); - } - - public static boolean greater(String ip1, String ip2) { - try { - IPAddress ipAddress1 = IPUtils.toAddress(ip1); - IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) > 0; - } catch (SemanticCheckException e) { - return false; - } - } - - public static boolean greater(String ip1, ExprIpValue ip2) { - String ipAddress2 = ip2.value(); - return greater(ip1, ipAddress2); - } - - public static boolean greater(ExprIpValue ip1, String ip2) { - String ipAddress1 = ip1.value(); - return greater(ipAddress1, ip2); - } - - public static boolean greater(ExprIpValue ip1, ExprIpValue ip2) { - String ipAddress1 = ip1.value(); - String ipAddress2 = ip2.value(); - return greater(ipAddress1, ipAddress2); - } - } -} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java deleted file mode 100644 index 769fac4a986..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/GteIpFunction.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -import inet.ipaddr.IPAddress; -import java.util.List; -import org.apache.calcite.adapter.enumerable.NotNullImplementor; -import org.apache.calcite.adapter.enumerable.NullPolicy; -import org.apache.calcite.adapter.enumerable.RexToLixTranslator; -import org.apache.calcite.linq4j.tree.Expression; -import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; -import org.opensearch.sql.data.model.ExprIpValue; -import org.opensearch.sql.exception.SemanticCheckException; -import org.opensearch.sql.expression.function.ImplementorUDF; -import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; - -/** - * {@code gte(ip1, ip2)} checks if ip1 is greater than or equals ip2. - * - *

              Signature: - * - *

                - *
              • (IP, STRING) -> BOOLEAN - *
              • (STRING, IP) -> BOOLEAN - *
              • (IP, IP) -> BOOLEAN - *
              - */ -public class GteIpFunction extends ImplementorUDF { - public GteIpFunction() { - super(new GteImplementor(), NullPolicy.ANY); - } - - @Override - public SqlReturnTypeInference getReturnTypeInference() { - return ReturnTypes.BOOLEAN_FORCE_NULLABLE; - } - - @Override - public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - } - - public static class GteImplementor implements NotNullImplementor { - @Override - public Expression implement( - RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(GteImplementor.class, "gte", translatedOperands); - } - - public static boolean gte(String ip1, String ip2) { - try { - IPAddress ipAddress1 = IPUtils.toAddress(ip1); - IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) >= 0; - } catch (SemanticCheckException e) { - return false; - } - } - - public static boolean gte(String ip1, ExprIpValue ip2) { - String ipAddress2 = ip2.value(); - return gte(ip1, ipAddress2); - } - - public static boolean gte(ExprIpValue ip1, String ip2) { - String ipAddress1 = ip1.value(); - return gte(ipAddress1, ip2); - } - - public static boolean gte(ExprIpValue ip1, ExprIpValue ip2) { - String ipAddress1 = ip1.value(); - String ipAddress2 = ip2.value(); - return gte(ipAddress1, ipAddress2); - } - } -} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java new file mode 100644 index 00000000000..024fd055259 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java @@ -0,0 +1,42 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.function.udf.ip; + +public class IpComparisonOperators { + + public enum ComparisonOperator { + EQUALS, + NOT_EQUALS, + LESS, + LESS_OR_EQUAL, + GREATER, + GREATER_OR_EQUAL + } + + public static CompareIpFunction equalsIp() { + return new CompareIpFunction(ComparisonOperator.EQUALS); + } + + public static CompareIpFunction notEqualsIp() { + return new CompareIpFunction(ComparisonOperator.NOT_EQUALS); + } + + public static CompareIpFunction lessThanIp() { + return new CompareIpFunction(ComparisonOperator.LESS); + } + + public static CompareIpFunction greaterThanIp() { + return new CompareIpFunction(ComparisonOperator.GREATER); + } + + public static CompareIpFunction lessOrEqualsIp() { + return new CompareIpFunction(ComparisonOperator.LESS_OR_EQUAL); + } + + public static CompareIpFunction greaterOrEqualsIp() { + return new CompareIpFunction(ComparisonOperator.GREATER_OR_EQUAL); + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java deleted file mode 100644 index e554a241173..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LessIpFunction.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -import inet.ipaddr.IPAddress; -import java.util.List; -import org.apache.calcite.adapter.enumerable.NotNullImplementor; -import org.apache.calcite.adapter.enumerable.NullPolicy; -import org.apache.calcite.adapter.enumerable.RexToLixTranslator; -import org.apache.calcite.linq4j.tree.Expression; -import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; -import org.opensearch.sql.data.model.ExprIpValue; -import org.opensearch.sql.exception.SemanticCheckException; -import org.opensearch.sql.expression.function.ImplementorUDF; -import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; - -/** - * {@code less(ip1, ip2)} checks if ip1 is less than ip2. - * - *

              Signature: - * - *

                - *
              • (IP, STRING) -> BOOLEAN - *
              • (STRING, IP) -> BOOLEAN - *
              • (IP, IP) -> BOOLEAN - *
              - */ -public class LessIpFunction extends ImplementorUDF { - public LessIpFunction() { - super(new LessImplementor(), NullPolicy.ANY); - } - - @Override - public SqlReturnTypeInference getReturnTypeInference() { - return ReturnTypes.BOOLEAN_FORCE_NULLABLE; - } - - @Override - public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - } - - public static class LessImplementor implements NotNullImplementor { - @Override - public Expression implement( - RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(LessImplementor.class, "less", translatedOperands); - } - - public static boolean less(String ip1, String ip2) { - try { - IPAddress ipAddress1 = IPUtils.toAddress(ip1); - IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) < 0; - } catch (SemanticCheckException e) { - return false; - } - } - - public static boolean less(String ip1, ExprIpValue ip2) { - String ipAddress2 = ip2.value(); - return less(ip1, ipAddress2); - } - - public static boolean less(ExprIpValue ip1, String ip2) { - String ipAddress1 = ip1.value(); - return less(ipAddress1, ip2); - } - - public static boolean less(ExprIpValue ip1, ExprIpValue ip2) { - String ipAddress1 = ip1.value(); - String ipAddress2 = ip2.value(); - return less(ipAddress1, ipAddress2); - } - } -} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java deleted file mode 100644 index 4b2f10391e2..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/LteIpFunction.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -import inet.ipaddr.IPAddress; -import java.util.List; -import org.apache.calcite.adapter.enumerable.NotNullImplementor; -import org.apache.calcite.adapter.enumerable.NullPolicy; -import org.apache.calcite.adapter.enumerable.RexToLixTranslator; -import org.apache.calcite.linq4j.tree.Expression; -import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; -import org.opensearch.sql.data.model.ExprIpValue; -import org.opensearch.sql.exception.SemanticCheckException; -import org.opensearch.sql.expression.function.ImplementorUDF; -import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; - -/** - * {@code lte(ip1, ip2)} checks if ip1 is less than or equals ip2. - * - *

              Signature: - * - *

                - *
              • (IP, STRING) -> BOOLEAN - *
              • (STRING, IP) -> BOOLEAN - *
              • (IP, IP) -> BOOLEAN - *
              - */ -public class LteIpFunction extends ImplementorUDF { - public LteIpFunction() { - super(new LteImplementor(), NullPolicy.ANY); - } - - @Override - public SqlReturnTypeInference getReturnTypeInference() { - return ReturnTypes.BOOLEAN_FORCE_NULLABLE; - } - - @Override - public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - } - - public static class LteImplementor implements NotNullImplementor { - @Override - public Expression implement( - RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(LteImplementor.class, "lte", translatedOperands); - } - - public static boolean lte(String ip1, String ip2) { - try { - IPAddress ipAddress1 = IPUtils.toAddress(ip1); - IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) <= 0; - } catch (SemanticCheckException e) { - return false; - } - } - - public static boolean lte(String ip1, ExprIpValue ip2) { - String ipAddress2 = ip2.value(); - return lte(ip1, ipAddress2); - } - - public static boolean lte(ExprIpValue ip1, String ip2) { - String ipAddress1 = ip1.value(); - return lte(ipAddress1, ip2); - } - - public static boolean lte(ExprIpValue ip1, ExprIpValue ip2) { - String ipAddress1 = ip1.value(); - String ipAddress2 = ip2.value(); - return lte(ipAddress1, ipAddress2); - } - } -} diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java deleted file mode 100644 index db7dc3e37e8..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/NotEqualsIpFunction.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -import inet.ipaddr.IPAddress; -import java.util.List; -import org.apache.calcite.adapter.enumerable.NotNullImplementor; -import org.apache.calcite.adapter.enumerable.NullPolicy; -import org.apache.calcite.adapter.enumerable.RexToLixTranslator; -import org.apache.calcite.linq4j.tree.Expression; -import org.apache.calcite.linq4j.tree.Expressions; -import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.*; -import org.opensearch.sql.data.model.ExprIpValue; -import org.opensearch.sql.exception.SemanticCheckException; -import org.opensearch.sql.expression.function.ImplementorUDF; -import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; - -/** - * {@code notEquals(ip1, ip2)} checks if two IP addresses are not equal. - * - *

              Signature: - * - *

                - *
              • (IP, STRING) -> BOOLEAN - *
              • (STRING, IP) -> BOOLEAN - *
              • (IP, IP) -> BOOLEAN - *
              - */ -public class NotEqualsIpFunction extends ImplementorUDF { - public NotEqualsIpFunction() { - super(new NotEqualsImplementor(), NullPolicy.ANY); - } - - @Override - public SqlReturnTypeInference getReturnTypeInference() { - return ReturnTypes.BOOLEAN_FORCE_NULLABLE; - } - - @Override - public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); - } - - public static class NotEqualsImplementor implements NotNullImplementor { - @Override - public Expression implement( - RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call(NotEqualsImplementor.class, "notEquals", translatedOperands); - } - - public static boolean notEquals(String ip1, String ip2) { - try { - IPAddress ipAddress1 = IPUtils.toAddress(ip1); - IPAddress ipAddress2 = IPUtils.toAddress(ip2); - return IPUtils.compare(ipAddress1, ipAddress2) != 0; - } catch (SemanticCheckException e) { - return true; - } - } - - public static boolean notEquals(String ip1, ExprIpValue ip2) { - String ipAddress2 = ip2.value(); - return notEquals(ip1, ipAddress2); - } - - public static boolean notEquals(ExprIpValue ip1, String ip2) { - String ipAddress1 = ip1.value(); - return notEquals(ipAddress1, ip2); - } - - public static boolean notEquals(ExprIpValue ip1, ExprIpValue ip2) { - String ipAddress1 = ip1.value(); - String ipAddress2 = ip2.value(); - return notEquals(ipAddress1, ipAddress2); - } - } -} From 0acc8f7ebc0fa2c6d558d4b34710f7f0fa970f6e Mon Sep 17 00:00:00 2001 From: Yuanchun Shen Date: Mon, 30 Jun 2025 15:21:51 +0800 Subject: [PATCH 10/15] Make IpComparisonOperators an inner enum of CompareIPFunction Signed-off-by: Yuanchun Shen --- .../function/PPLBuiltinOperators.java | 16 ++-- .../function/udf/ip/CompareIpFunction.java | 76 ++++++++++++------- .../udf/ip/IpComparisonOperators.java | 42 ---------- 3 files changed, 55 insertions(+), 79 deletions(-) delete mode 100644 core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java index a5ddcf7bdf4..f295b07022c 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java @@ -71,7 +71,7 @@ import org.opensearch.sql.expression.function.udf.datetime.WeekdayFunction; import org.opensearch.sql.expression.function.udf.datetime.YearweekFunction; import org.opensearch.sql.expression.function.udf.ip.CidrMatchFunction; -import org.opensearch.sql.expression.function.udf.ip.IpComparisonOperators; +import org.opensearch.sql.expression.function.udf.ip.CompareIpFunction; import org.opensearch.sql.expression.function.udf.math.CRC32Function; import org.opensearch.sql.expression.function.udf.math.ConvFunction; import org.opensearch.sql.expression.function.udf.math.DivideFunction; @@ -105,14 +105,12 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable { // IP comparing functions public static final SqlOperator NOT_EQUALS_IP = - IpComparisonOperators.notEqualsIp().toUDF("NOT_EQUALS_IP"); - public static final SqlOperator EQUALS_IP = IpComparisonOperators.equalsIp().toUDF("EQUALS_IP"); - public static final SqlOperator GREATER_IP = - IpComparisonOperators.greaterThanIp().toUDF("GREATER_IP"); - public static final SqlOperator GTE_IP = - IpComparisonOperators.greaterOrEqualsIp().toUDF("GTE_IP"); - public static final SqlOperator LESS_IP = IpComparisonOperators.lessThanIp().toUDF("LESS_IP"); - public static final SqlOperator LTE_IP = IpComparisonOperators.lessOrEqualsIp().toUDF("LTE_IP"); + CompareIpFunction.notEquals().toUDF("NOT_EQUALS_IP"); + public static final SqlOperator EQUALS_IP = CompareIpFunction.equals().toUDF("EQUALS_IP"); + public static final SqlOperator GREATER_IP = CompareIpFunction.greater().toUDF("GREATER_IP"); + public static final SqlOperator GTE_IP = CompareIpFunction.greaterOrEquals().toUDF("GTE_IP"); + public static final SqlOperator LESS_IP = CompareIpFunction.less().toUDF("LESS_IP"); + public static final SqlOperator LTE_IP = CompareIpFunction.lessOrEquals().toUDF("LTE_IP"); // Condition function public static final SqlOperator EARLIEST = new EarliestFunction().toUDF("EARLIEST"); diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java index 7fadf5ac675..12a6a42516d 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java @@ -35,11 +35,33 @@ *
            */ public class CompareIpFunction extends ImplementorUDF { - private final IpComparisonOperators.ComparisonOperator operator; - public CompareIpFunction(IpComparisonOperators.ComparisonOperator operator) { - super(new CompareImplementor(operator), NullPolicy.ANY); - this.operator = operator; + private CompareIpFunction(ComparisonType comparisonType) { + super(new CompareImplementor(comparisonType), NullPolicy.ANY); + } + + public static CompareIpFunction less() { + return new CompareIpFunction(ComparisonType.LESS); + } + + public static CompareIpFunction greater() { + return new CompareIpFunction(ComparisonType.GREATER); + } + + public static CompareIpFunction lessOrEquals() { + return new CompareIpFunction(ComparisonType.LESS_OR_EQUAL); + } + + public static CompareIpFunction greaterOrEquals() { + return new CompareIpFunction(ComparisonType.GREATER_OR_EQUAL); + } + + public static CompareIpFunction equals() { + return new CompareIpFunction(ComparisonType.EQUALS); + } + + public static CompareIpFunction notEquals() { + return new CompareIpFunction(ComparisonType.NOT_EQUALS); } @Override @@ -57,10 +79,10 @@ public UDFOperandMetadata getOperandMetadata() { } public static class CompareImplementor implements NotNullImplementor { - private final IpComparisonOperators.ComparisonOperator operator; + private final ComparisonType comparisonType; - public CompareImplementor(IpComparisonOperators.ComparisonOperator operator) { - this.operator = operator; + public CompareImplementor(ComparisonType comparisonType) { + this.comparisonType = comparisonType; } @Override @@ -71,35 +93,24 @@ public Expression implement( "compare", translatedOperands.get(0), translatedOperands.get(1), - Expressions.constant(operator.name())); + Expressions.constant(comparisonType)); } - public static boolean compare(Object obj1, Object obj2, String opName) { + public static boolean compare(Object obj1, Object obj2, ComparisonType comparisonType) { try { - IpComparisonOperators.ComparisonOperator op = - IpComparisonOperators.ComparisonOperator.valueOf(opName); String ip1 = extractIpString(obj1); String ip2 = extractIpString(obj2); IPAddress addr1 = IPUtils.toAddress(ip1); IPAddress addr2 = IPUtils.toAddress(ip2); int result = IPUtils.compare(addr1, addr2); - - switch (op) { - case EQUALS: - return result == 0; - case NOT_EQUALS: - return result != 0; - case LESS: - return result < 0; - case LESS_OR_EQUAL: - return result <= 0; - case GREATER: - return result > 0; - case GREATER_OR_EQUAL: - return result >= 0; - default: - return false; - } + return switch (comparisonType) { + case EQUALS -> result == 0; + case NOT_EQUALS -> result != 0; + case LESS -> result < 0; + case LESS_OR_EQUAL -> result <= 0; + case GREATER -> result > 0; + case GREATER_OR_EQUAL -> result >= 0; + }; } catch (Exception e) { return false; } @@ -111,4 +122,13 @@ private static String extractIpString(Object obj) { throw new IllegalArgumentException("Invalid IP type: " + obj); } } + + public enum ComparisonType { + EQUALS, + NOT_EQUALS, + LESS, + LESS_OR_EQUAL, + GREATER, + GREATER_OR_EQUAL + } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java deleted file mode 100644 index 024fd055259..00000000000 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/IpComparisonOperators.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.opensearch.sql.expression.function.udf.ip; - -public class IpComparisonOperators { - - public enum ComparisonOperator { - EQUALS, - NOT_EQUALS, - LESS, - LESS_OR_EQUAL, - GREATER, - GREATER_OR_EQUAL - } - - public static CompareIpFunction equalsIp() { - return new CompareIpFunction(ComparisonOperator.EQUALS); - } - - public static CompareIpFunction notEqualsIp() { - return new CompareIpFunction(ComparisonOperator.NOT_EQUALS); - } - - public static CompareIpFunction lessThanIp() { - return new CompareIpFunction(ComparisonOperator.LESS); - } - - public static CompareIpFunction greaterThanIp() { - return new CompareIpFunction(ComparisonOperator.GREATER); - } - - public static CompareIpFunction lessOrEqualsIp() { - return new CompareIpFunction(ComparisonOperator.LESS_OR_EQUAL); - } - - public static CompareIpFunction greaterOrEqualsIp() { - return new CompareIpFunction(ComparisonOperator.GREATER_OR_EQUAL); - } -} From fb8e96983780b994068c7e6a8a4233aebb265ea2 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Thu, 3 Jul 2025 15:53:28 +0800 Subject: [PATCH 11/15] modify registerOperator Signed-off-by: Xinyu Hao --- .../expression/function/PPLFuncImpTable.java | 91 +++++++++---------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index 981f3be585f..937ab8a8adc 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -473,40 +473,45 @@ private abstract static class AbstractBuilder { /** Maps an operator to an implementation. */ abstract void register(BuiltinFunctionName functionName, FunctionImp functionImp); - void registerOperator(BuiltinFunctionName functionName, SqlOperator operator) { - SqlOperandTypeChecker typeChecker; - if (operator instanceof SqlUserDefinedFunction udfOperator) { - typeChecker = extractTypeCheckerFromUDF(udfOperator); - } else { - typeChecker = operator.getOperandTypeChecker(); - } + public void registerOperator(BuiltinFunctionName functionName, SqlOperator... operators) { + for (SqlOperator operator : operators) { + SqlOperandTypeChecker typeChecker; + if (operator instanceof SqlUserDefinedFunction udfOperator) { + typeChecker = extractTypeCheckerFromUDF(udfOperator); + } else { + typeChecker = operator.getOperandTypeChecker(); + } - // Only the composite operand type checker for UDFs are concerned here. - if (operator instanceof SqlUserDefinedFunction - && typeChecker instanceof CompositeOperandTypeChecker compositeTypeChecker) { - // UDFs implement their own composite type checkers, which always use OR logic for argument - // types. Verifying the composition type would require accessing a protected field in - // CompositeOperandTypeChecker. If access to this field is not allowed, type checking will - // be skipped, so we avoid checking the composition type here. - register(functionName, wrapWithCompositeTypeChecker(operator, compositeTypeChecker, false)); - } else if (typeChecker instanceof ImplicitCastOperandTypeChecker implicitCastTypeChecker) { - register(functionName, wrapWithImplicitCastTypeChecker(operator, implicitCastTypeChecker)); - } else if (typeChecker instanceof CompositeOperandTypeChecker compositeTypeChecker) { - // If compositeTypeChecker contains operand checkers other than family type checkers or - // other than OR compositions, the function with be registered with a null type checker, - // which means the function will not be type checked. - register(functionName, wrapWithCompositeTypeChecker(operator, compositeTypeChecker, true)); - } else if (typeChecker instanceof SameOperandTypeChecker comparableTypeChecker) { - // Comparison operators like EQUAL, GREATER_THAN, LESS_THAN, etc. - // SameOperandTypeCheckers like COALESCE, IFNULL, etc. - register(functionName, wrapWithComparableTypeChecker(operator, comparableTypeChecker)); - } else { - logger.info( - "Cannot create type checker for function: {}. Will skip its type checking", - functionName); - register( - functionName, - (RexBuilder builder, RexNode... node) -> builder.makeCall(operator, node)); + if (operator instanceof SqlUserDefinedFunction + && typeChecker instanceof CompositeOperandTypeChecker compositeTypeChecker) { + // UDFs implement their own composite type checkers, which always use OR logic for + // argument + // types. Verifying the composition type would require accessing a protected field in + // CompositeOperandTypeChecker. If access to this field is not allowed, type checking will + // be skipped, so we avoid checking the composition type here. + register( + functionName, wrapWithCompositeTypeChecker(operator, compositeTypeChecker, false)); + } else if (typeChecker instanceof ImplicitCastOperandTypeChecker implicitCastTypeChecker) { + register( + functionName, wrapWithImplicitCastTypeChecker(operator, implicitCastTypeChecker)); + } else if (typeChecker instanceof CompositeOperandTypeChecker compositeTypeChecker) { + // If compositeTypeChecker contains operand checkers other than family type checkers or + // other than OR compositions, the function with be registered with a null type checker, + // which means the function will not be type checked. + register( + functionName, wrapWithCompositeTypeChecker(operator, compositeTypeChecker, true)); + } else if (typeChecker instanceof SameOperandTypeChecker comparableTypeChecker) { + // Comparison operators like EQUAL, GREATER_THAN, LESS_THAN, etc. + // SameOperandTypeCheckers like COALESCE, IFNULL, etc. + register(functionName, wrapWithComparableTypeChecker(operator, comparableTypeChecker)); + } else { + logger.info( + "Cannot create type checker for function: {}. Will skip its type checking", + functionName); + register( + functionName, + (RexBuilder builder, RexNode... node) -> builder.makeCall(operator, node)); + } } } @@ -614,24 +619,18 @@ public PPLTypeChecker getTypeChecker() { } void populate() { - // register operators for IP comparing - registerOperator(NOTEQUAL, PPLBuiltinOperators.NOT_EQUALS_IP); - registerOperator(EQUAL, PPLBuiltinOperators.EQUALS_IP); - registerOperator(GREATER, PPLBuiltinOperators.GREATER_IP); - registerOperator(GTE, PPLBuiltinOperators.GTE_IP); - registerOperator(LESS, PPLBuiltinOperators.LESS_IP); - registerOperator(LTE, PPLBuiltinOperators.LTE_IP); + // register operators for comparison + registerOperator(NOTEQUAL, PPLBuiltinOperators.NOT_EQUALS_IP, SqlStdOperatorTable.NOT_EQUALS); + registerOperator(EQUAL, PPLBuiltinOperators.EQUALS_IP, SqlStdOperatorTable.EQUALS); + registerOperator(GREATER, PPLBuiltinOperators.GREATER_IP, SqlStdOperatorTable.GREATER_THAN); + registerOperator(GTE, PPLBuiltinOperators.GTE_IP, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL); + registerOperator(LESS, PPLBuiltinOperators.LESS_IP, SqlStdOperatorTable.LESS_THAN); + registerOperator(LTE, PPLBuiltinOperators.LTE_IP, SqlStdOperatorTable.LESS_THAN_OR_EQUAL); // Register std operator registerOperator(AND, SqlStdOperatorTable.AND); registerOperator(OR, SqlStdOperatorTable.OR); registerOperator(NOT, SqlStdOperatorTable.NOT); - registerOperator(NOTEQUAL, SqlStdOperatorTable.NOT_EQUALS); - registerOperator(EQUAL, SqlStdOperatorTable.EQUALS); - registerOperator(GREATER, SqlStdOperatorTable.GREATER_THAN); - registerOperator(GTE, SqlStdOperatorTable.GREATER_THAN_OR_EQUAL); - registerOperator(LESS, SqlStdOperatorTable.LESS_THAN); - registerOperator(LTE, SqlStdOperatorTable.LESS_THAN_OR_EQUAL); registerOperator(ADD, SqlStdOperatorTable.PLUS); registerOperator(SUBTRACT, SqlStdOperatorTable.MINUS); registerOperator(MULTIPLY, SqlStdOperatorTable.MULTIPLY); From 999fdf7a2e38c49b09fd4f26a6c38a5fd5317651 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Thu, 3 Jul 2025 15:57:20 +0800 Subject: [PATCH 12/15] modify registerOperator Signed-off-by: Xinyu Hao --- .../org/opensearch/sql/expression/function/PPLFuncImpTable.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index 937ab8a8adc..1713699022f 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -482,6 +482,7 @@ public void registerOperator(BuiltinFunctionName functionName, SqlOperator... op typeChecker = operator.getOperandTypeChecker(); } + // Only the composite operand type checker for UDFs are concerned here. if (operator instanceof SqlUserDefinedFunction && typeChecker instanceof CompositeOperandTypeChecker compositeTypeChecker) { // UDFs implement their own composite type checkers, which always use OR logic for From 5151350cbd10517d7ef371493268659e2a69af00 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Fri, 4 Jul 2025 16:18:50 +0800 Subject: [PATCH 13/15] add type checker for cidr Signed-off-by: Xinyu Hao --- .../utils/UserDefinedFunctionUtils.java | 7 +- .../expression/function/PPLFuncImpTable.java | 17 +++- .../expression/function/PPLTypeChecker.java | 65 ++++++++---- .../function/UDFOperandMetadata.java | 99 ++++++++++++++----- .../function/udf/ip/CidrMatchFunction.java | 11 +-- .../function/udf/ip/CompareIpFunction.java | 9 +- .../calcite/CalcitePPLFunctionTypeTest.java | 3 +- 7 files changed, 145 insertions(+), 66 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java index f220c2265b2..163ac108392 100644 --- a/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java +++ b/core/src/main/java/org/opensearch/sql/calcite/utils/UserDefinedFunctionUtils.java @@ -115,10 +115,9 @@ public static SqlTypeName convertRelDataTypeToSqlTypeName(RelDataType type) { case EXPR_DATE -> SqlTypeName.DATE; case EXPR_TIME -> SqlTypeName.TIME; case EXPR_TIMESTAMP -> SqlTypeName.TIMESTAMP; - // EXPR_IP is mapped to SqlTypeName.NULL since there is no - // corresponding SqlTypeName in Calcite. This is a workaround to allow - // type checking for IP types in UDFs. - case EXPR_IP -> SqlTypeName.NULL; + // EXPR_IP is mapped to SqlTypeName.OTHER since there is no + // corresponding SqlTypeName in Calcite. + case EXPR_IP -> SqlTypeName.OTHER; case EXPR_BINARY -> SqlTypeName.VARBINARY; default -> type.getSqlTypeName(); }; diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index 1713699022f..994c2a3a8f9 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -450,7 +450,10 @@ functionName, getActualSignature(argTypes), e.getMessage()), } StringJoiner allowedSignatures = new StringJoiner(","); for (var implement : implementList) { - allowedSignatures.add(implement.getKey().typeChecker().getAllowedSignatures()); + String signature = implement.getKey().typeChecker().getAllowedSignatures(); + if (!signature.isEmpty()) { + allowedSignatures.add(signature); + } } throw new ExpressionEvaluationException( String.format( @@ -505,6 +508,18 @@ public void registerOperator(BuiltinFunctionName functionName, SqlOperator... op // Comparison operators like EQUAL, GREATER_THAN, LESS_THAN, etc. // SameOperandTypeCheckers like COALESCE, IFNULL, etc. register(functionName, wrapWithComparableTypeChecker(operator, comparableTypeChecker)); + } else if (typeChecker instanceof UDFOperandMetadata.IPOperandMetadata) { + register( + functionName, + createFunctionImpWithTypeChecker( + (builder, arg1, arg2) -> builder.makeCall(operator, arg1, arg2), + new PPLTypeChecker.PPLIPCompareTypeChecker())); + } else if (typeChecker instanceof UDFOperandMetadata.CidrOperandMetadata) { + register( + functionName, + createFunctionImpWithTypeChecker( + (builder, arg1, arg2) -> builder.makeCall(operator, arg1, arg2), + new PPLTypeChecker.PPLCidrTypeChecker())); } else { logger.info( "Cannot create type checker for function: {}. Will skip its type checking", diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java index 17e9e290add..b3de4de4f51 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLTypeChecker.java @@ -23,7 +23,7 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; -import org.opensearch.sql.calcite.type.AbstractExprRelDataType; +import org.opensearch.sql.calcite.type.ExprIPType; import org.opensearch.sql.calcite.utils.OpenSearchTypeFactory; import org.opensearch.sql.calcite.utils.UserDefinedFunctionUtils; import org.opensearch.sql.data.type.ExprCoreType; @@ -215,10 +215,6 @@ public boolean checkOperandTypes(List types) { RelDataType type_l = types.get(i); RelDataType type_r = types.get(i + 1); if (!SqlTypeUtil.isComparable(type_l, type_r)) { - if (areIpAndStringTypes(type_l, type_r) || areIpAndStringTypes(type_r, type_l)) { - // Allow IP and string comparison - continue; - } return false; } // Disallow coercing between strings and numeric, boolean @@ -239,14 +235,6 @@ private static boolean cannotConvertStringInCompare(SqlTypeFamily typeFamily) { }; } - private static boolean areIpAndStringTypes(RelDataType typeIp, RelDataType typeString) { - if (typeIp instanceof AbstractExprRelDataType exprRelDataType) { - return exprRelDataType.getExprType() == ExprCoreType.IP - && typeString.getFamily() == SqlTypeFamily.CHARACTER; - } - return false; - } - @Override public String getAllowedSignatures() { int min = innerTypeChecker.getOperandCountRange().getMin(); @@ -269,6 +257,53 @@ public String getAllowedSignatures() { } } + class PPLIPCompareTypeChecker implements PPLTypeChecker { + @Override + public boolean checkOperandTypes(List types) { + if (types.size() != 2) { + return false; + } + RelDataType type1 = types.get(0); + RelDataType type2 = types.get(1); + return areIpAndStringTypes(type1, type2) + || areIpAndStringTypes(type2, type1) + || (type1 instanceof ExprIPType && type2 instanceof ExprIPType); + } + + @Override + public String getAllowedSignatures() { + // Will be merged with the allowed signatures of comparable type checker, + // shown as [COMPARABLE_TYPE,COMPARABLE_TYPE] + return ""; + } + + private static boolean areIpAndStringTypes(RelDataType typeIp, RelDataType typeString) { + return typeIp instanceof ExprIPType && typeString.getFamily() == SqlTypeFamily.CHARACTER; + } + } + + class PPLCidrTypeChecker implements PPLTypeChecker { + @Override + public boolean checkOperandTypes(List types) { + if (types.size() != 2) { + return false; + } + RelDataType type1 = types.get(0); + RelDataType type2 = types.get(1); + + // accept (STRING, STRING) or (IP, STRING) + if (type2.getFamily() != SqlTypeFamily.CHARACTER) { + return false; + } + return type1 instanceof ExprIPType || type1.getFamily() == SqlTypeFamily.CHARACTER; + } + + @Override + public String getAllowedSignatures() { + return "[STRING,STRING],[IP,STRING]"; + } + } + /** * Creates a {@link PPLFamilyTypeChecker} with a fixed operand count, validating that each operand * belongs to its corresponding {@link SqlTypeFamily}. @@ -400,10 +435,6 @@ private static List getExprTypes(SqlTypeFamily family) { OpenSearchTypeFactory.TYPE_FACTORY.createSqlType(SqlTypeName.INTEGER)); case ANY, IGNORE -> List.of( OpenSearchTypeFactory.TYPE_FACTORY.createSqlType(SqlTypeName.ANY)); - // We borrow SqlTypeFamily.NULL to represent EXPR_IP. This is a workaround - // since there is no corresponding IP type family in Calcite. - case NULL -> List.of( - OpenSearchTypeFactory.TYPE_FACTORY.createUDT(OpenSearchTypeFactory.ExprUDT.EXPR_IP)); default -> { RelDataType type = family.getDefaultConcreteType(OpenSearchTypeFactory.TYPE_FACTORY); if (type == null) { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/UDFOperandMetadata.java b/core/src/main/java/org/opensearch/sql/expression/function/UDFOperandMetadata.java index fcd7a6a2be5..a7d12cbbbaf 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/UDFOperandMetadata.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/UDFOperandMetadata.java @@ -14,10 +14,8 @@ import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.type.CompositeOperandTypeChecker; import org.apache.calcite.sql.type.FamilyOperandTypeChecker; -import org.apache.calcite.sql.type.ImplicitCastOperandTypeChecker; import org.apache.calcite.sql.type.SqlOperandMetadata; import org.apache.calcite.sql.type.SqlOperandTypeChecker; -import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.validate.SqlUserDefinedFunction; /** @@ -25,7 +23,7 @@ * creating UDFs, so that a type checker can be passed to the constructor of {@link * SqlUserDefinedFunction} as a {@link SqlOperandMetadata}. */ -public interface UDFOperandMetadata extends SqlOperandMetadata, ImplicitCastOperandTypeChecker { +public interface UDFOperandMetadata extends SqlOperandMetadata { SqlOperandTypeChecker getInnerTypeChecker(); static UDFOperandMetadata wrap(FamilyOperandTypeChecker typeChecker) { @@ -35,17 +33,6 @@ public SqlOperandTypeChecker getInnerTypeChecker() { return typeChecker; } - @Override - public boolean checkOperandTypesWithoutTypeCoercion( - SqlCallBinding callBinding, boolean throwOnFailure) { - return typeChecker.checkOperandTypesWithoutTypeCoercion(callBinding, throwOnFailure); - } - - @Override - public SqlTypeFamily getOperandSqlTypeFamily(int iFormalOperand) { - return typeChecker.getOperandSqlTypeFamily(iFormalOperand); - } - @Override public List paramTypes(RelDataTypeFactory typeFactory) { // This function is not used in the current context, so we return an empty list. @@ -89,18 +76,6 @@ public SqlOperandTypeChecker getInnerTypeChecker() { return typeChecker; } - @Override - public boolean checkOperandTypesWithoutTypeCoercion( - SqlCallBinding callBinding, boolean throwOnFailure) { - return typeChecker.checkOperandTypes(callBinding, throwOnFailure); - } - - @Override - public SqlTypeFamily getOperandSqlTypeFamily(int iFormalOperand) { - throw new IllegalStateException( - "getOperandSqlTypeFamily is not supported for CompositeOperandTypeChecker"); - } - @Override public List paramTypes(RelDataTypeFactory typeFactory) { // This function is not used in the current context, so we return an empty list. @@ -129,4 +104,76 @@ public String getAllowedSignatures(SqlOperator op, String opName) { } }; } + + /** + * A named class that serves as an identifier for IP comparator's operand metadata. It does not + * implement any actual type checking logic. + */ + class IPOperandMetadata implements UDFOperandMetadata { + @Override + public SqlOperandTypeChecker getInnerTypeChecker() { + return this; + } + + @Override + public List paramTypes(RelDataTypeFactory typeFactory) { + return List.of(); + } + + @Override + public List paramNames() { + return List.of(); + } + + @Override + public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) { + return false; + } + + @Override + public SqlOperandCountRange getOperandCountRange() { + return null; + } + + @Override + public String getAllowedSignatures(SqlOperator op, String opName) { + return ""; + } + } + + /** + * A named class that serves as an identifier for cidr's operand metadata. It does not implement + * any actual type checking logic. + */ + class CidrOperandMetadata implements UDFOperandMetadata { + @Override + public SqlOperandTypeChecker getInnerTypeChecker() { + return this; + } + + @Override + public List paramTypes(RelDataTypeFactory typeFactory) { + return List.of(); + } + + @Override + public List paramNames() { + return List.of(); + } + + @Override + public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) { + return false; + } + + @Override + public SqlOperandCountRange getOperandCountRange() { + return null; + } + + @Override + public String getAllowedSignatures(SqlOperator op, String opName) { + return ""; + } + } } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java index 4c8e532cbb0..bba375079a7 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CidrMatchFunction.java @@ -12,11 +12,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.Expressions; import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.CompositeOperandTypeChecker; -import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlReturnTypeInference; -import org.apache.calcite.sql.type.SqlTypeFamily; import org.opensearch.sql.data.model.ExprIpValue; import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.data.model.ExprValueUtils; @@ -46,12 +43,10 @@ public SqlReturnTypeInference getReturnTypeInference() { @Override public UDFOperandMetadata getOperandMetadata() { - // EXPR_IP is mapped to SqlTypeFamily.NULL in + // EXPR_IP is mapped to SqlTypeFamily.OTHER in // UserDefinedFunctionUtils.convertRelDataTypeToSqlTypeName - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.STRING) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING))); + // We use a specific type checker to serve + return new UDFOperandMetadata.CidrOperandMetadata(); } public static class CidrMatchImplementor implements NotNullImplementor { diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java index 12a6a42516d..9704d0dbd08 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java @@ -13,11 +13,8 @@ import org.apache.calcite.linq4j.tree.Expression; import org.apache.calcite.linq4j.tree.Expressions; import org.apache.calcite.rex.RexCall; -import org.apache.calcite.sql.type.CompositeOperandTypeChecker; -import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlReturnTypeInference; -import org.apache.calcite.sql.type.SqlTypeFamily; import org.opensearch.sql.data.model.ExprIpValue; import org.opensearch.sql.expression.function.ImplementorUDF; import org.opensearch.sql.expression.function.UDFOperandMetadata; @@ -71,11 +68,7 @@ public SqlReturnTypeInference getReturnTypeInference() { @Override public UDFOperandMetadata getOperandMetadata() { - return UDFOperandMetadata.wrap( - (CompositeOperandTypeChecker) - OperandTypes.family(SqlTypeFamily.STRING, SqlTypeFamily.NULL) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.STRING)) - .or(OperandTypes.family(SqlTypeFamily.NULL, SqlTypeFamily.NULL))); + return new UDFOperandMetadata.IPOperandMetadata(); } public static class CompareImplementor implements NotNullImplementor { diff --git a/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java b/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java index 20447be761a..ef1237fa2ec 100644 --- a/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java +++ b/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLFunctionTypeTest.java @@ -50,8 +50,7 @@ public void testComparisonWithDifferentType() { Throwable t = Assert.assertThrows(ExpressionEvaluationException.class, () -> getRelNode(ppl)); verifyErrorMessageContains( t, - "LESS function expects {[STRING,IP],[IP,STRING],[IP,IP],[COMPARABLE_TYPE,COMPARABLE_TYPE]}," - + " but got [STRING,INTEGER]"); + "LESS function expects {[COMPARABLE_TYPE,COMPARABLE_TYPE]}," + " but got [STRING,INTEGER]"); } @Test From e2a124f53f0e55eba6612e3a999e311faca7e683 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Mon, 7 Jul 2025 11:06:17 +0800 Subject: [PATCH 14/15] add javadoc Signed-off-by: Xinyu Hao --- .../sql/expression/function/PPLFuncImpTable.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java index 994c2a3a8f9..d02596f4f0d 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java @@ -476,6 +476,18 @@ private abstract static class AbstractBuilder { /** Maps an operator to an implementation. */ abstract void register(BuiltinFunctionName functionName, FunctionImp functionImp); + /** + * Register one or multiple operators under a single function name. This allows function + * overloading based on operand types. + * + *

            When a function is called, the system will try each registered operator in sequence, + * checking if the provided arguments match the operator's type requirements. The first operator + * whose type checker accepts the arguments will be used to execute the function. + * + * @param functionName the built-in function name under which to register the operators + * @param operators the operators to associate with this function name, tried in sequence until + * one matches the argument types during resolution + */ public void registerOperator(BuiltinFunctionName functionName, SqlOperator... operators) { for (SqlOperator operator : operators) { SqlOperandTypeChecker typeChecker; From 62a60848093bd84c7a854907173b2afc419dbf83 Mon Sep 17 00:00:00 2001 From: Xinyu Hao Date: Mon, 7 Jul 2025 13:20:40 +0800 Subject: [PATCH 15/15] move switch case to the implement method Signed-off-by: Xinyu Hao --- .../function/udf/ip/CompareIpFunction.java | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java index 9704d0dbd08..3821ec1695a 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ip/CompareIpFunction.java @@ -5,7 +5,6 @@ package org.opensearch.sql.expression.function.udf.ip; -import inet.ipaddr.IPAddress; import java.util.List; import org.apache.calcite.adapter.enumerable.NotNullImplementor; import org.apache.calcite.adapter.enumerable.NullPolicy; @@ -18,7 +17,6 @@ import org.opensearch.sql.data.model.ExprIpValue; import org.opensearch.sql.expression.function.ImplementorUDF; import org.opensearch.sql.expression.function.UDFOperandMetadata; -import org.opensearch.sql.utils.IPUtils; /** * {@code compare(ip1, ip2)} compares two IP addresses using a provided op. @@ -81,37 +79,41 @@ public CompareImplementor(ComparisonType comparisonType) { @Override public Expression implement( RexToLixTranslator translator, RexCall call, List translatedOperands) { - return Expressions.call( - CompareImplementor.class, - "compare", - translatedOperands.get(0), - translatedOperands.get(1), - Expressions.constant(comparisonType)); + Expression compareResult = + Expressions.call( + CompareImplementor.class, + "compareTo", + translatedOperands.get(0), + translatedOperands.get(1)); + + return generateComparisonExpression(compareResult, comparisonType); } - public static boolean compare(Object obj1, Object obj2, ComparisonType comparisonType) { - try { - String ip1 = extractIpString(obj1); - String ip2 = extractIpString(obj2); - IPAddress addr1 = IPUtils.toAddress(ip1); - IPAddress addr2 = IPUtils.toAddress(ip2); - int result = IPUtils.compare(addr1, addr2); - return switch (comparisonType) { - case EQUALS -> result == 0; - case NOT_EQUALS -> result != 0; - case LESS -> result < 0; - case LESS_OR_EQUAL -> result <= 0; - case GREATER -> result > 0; - case GREATER_OR_EQUAL -> result >= 0; - }; - } catch (Exception e) { - return false; - } + private static Expression generateComparisonExpression( + Expression compareResult, ComparisonType comparisonType) { + return switch (comparisonType) { + case EQUALS -> Expressions.equal(compareResult, Expressions.constant(0)); + case NOT_EQUALS -> Expressions.notEqual(compareResult, Expressions.constant(0)); + case LESS -> Expressions.lessThan(compareResult, Expressions.constant(0)); + case LESS_OR_EQUAL -> Expressions.lessThanOrEqual(compareResult, Expressions.constant(0)); + case GREATER -> Expressions.greaterThan(compareResult, Expressions.constant(0)); + case GREATER_OR_EQUAL -> Expressions.greaterThanOrEqual( + compareResult, Expressions.constant(0)); + }; + } + + public static int compareTo(Object obj1, Object obj2) { + ExprIpValue v1 = toExprIpValue(obj1); + ExprIpValue v2 = toExprIpValue(obj2); + return v1.compare(v2); } - private static String extractIpString(Object obj) { - if (obj instanceof String) return (String) obj; - if (obj instanceof ExprIpValue) return ((ExprIpValue) obj).value(); + private static ExprIpValue toExprIpValue(Object obj) { + if (obj instanceof ExprIpValue) { + return (ExprIpValue) obj; + } else if (obj instanceof String) { + return new ExprIpValue((String) obj); + } throw new IllegalArgumentException("Invalid IP type: " + obj); } }