Skip to content

Commit 730f066

Browse files
authored
Implement cidrmatch udf with Calcite (opensearch-project#3603)
* Implement cidrmatch UDF Signed-off-by: Yuanchun Shen <yuanchu@amazon.com> * Add class documentation for cidrmatch udf Signed-off-by: Yuanchun Shen <yuanchu@amazon.com> * Update cidrmatch doc: add IP as an acceptable parameter type Signed-off-by: Yuanchun Shen <yuanchu@amazon.com> --------- Signed-off-by: Yuanchun Shen <yuanchu@amazon.com>
1 parent e4838ec commit 730f066

6 files changed

Lines changed: 130 additions & 2 deletions

File tree

core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.opensearch.sql.expression.function.udf.datetime.WeekFunction;
4848
import org.opensearch.sql.expression.function.udf.datetime.WeekdayFunction;
4949
import org.opensearch.sql.expression.function.udf.datetime.YearweekFunction;
50+
import org.opensearch.sql.expression.function.udf.ip.CidrMatchFunction;
5051
import org.opensearch.sql.expression.function.udf.math.CRC32Function;
5152
import org.opensearch.sql.expression.function.udf.math.ConvFunction;
5253
import org.opensearch.sql.expression.function.udf.math.DivideFunction;
@@ -64,6 +65,7 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable {
6465
public static final SqlOperator CRC32 = new CRC32Function().toUDF("CRC32");
6566
public static final SqlOperator DIVIDE = new DivideFunction().toUDF("DIVIDE");
6667
public static final SqlOperator SHA2 = CryptographicFunction.sha2().toUDF("SHA2");
68+
public static final SqlOperator CIDRMATCH = new CidrMatchFunction().toUDF("CIDRMATCH");
6769

6870
// Datetime function
6971
public static final SqlOperator TIMESTAMP = new TimestampFunction().toUDF("TIMESTAMP");

core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ void populate() {
231231
registerOperator(CRC32, PPLBuiltinOperators.CRC32);
232232
registerOperator(DIVIDE, PPLBuiltinOperators.DIVIDE);
233233
registerOperator(SHA2, PPLBuiltinOperators.SHA2);
234+
registerOperator(CIDRMATCH, PPLBuiltinOperators.CIDRMATCH);
234235

235236
// Register PPL Datetime UDF operator
236237
registerOperator(TIMESTAMP, PPLBuiltinOperators.TIMESTAMP);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.expression.function.udf.ip;
7+
8+
import java.util.List;
9+
import org.apache.calcite.adapter.enumerable.NotNullImplementor;
10+
import org.apache.calcite.adapter.enumerable.NullPolicy;
11+
import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
12+
import org.apache.calcite.linq4j.tree.Expression;
13+
import org.apache.calcite.linq4j.tree.Expressions;
14+
import org.apache.calcite.rex.RexCall;
15+
import org.apache.calcite.sql.type.ReturnTypes;
16+
import org.apache.calcite.sql.type.SqlReturnTypeInference;
17+
import org.opensearch.sql.data.model.ExprIpValue;
18+
import org.opensearch.sql.data.model.ExprValue;
19+
import org.opensearch.sql.data.model.ExprValueUtils;
20+
import org.opensearch.sql.expression.function.ImplementorUDF;
21+
import org.opensearch.sql.expression.ip.IPFunctions;
22+
23+
/**
24+
* {@code cidrmatch(ip, cidr)} checks if ip is within the specified cidr range.
25+
*
26+
* <p>Signature:
27+
*
28+
* <ul>
29+
* <li>(STRING, STRING) -> BOOLEAN
30+
* <li>(IP, STRING) -> BOOLEAN
31+
* </ul>
32+
*/
33+
public class CidrMatchFunction extends ImplementorUDF {
34+
public CidrMatchFunction() {
35+
super(new CidrMatchImplementor(), NullPolicy.ANY);
36+
}
37+
38+
@Override
39+
public SqlReturnTypeInference getReturnTypeInference() {
40+
return ReturnTypes.BOOLEAN_FORCE_NULLABLE;
41+
}
42+
43+
public static class CidrMatchImplementor implements NotNullImplementor {
44+
@Override
45+
public Expression implement(
46+
RexToLixTranslator translator, RexCall call, List<Expression> translatedOperands) {
47+
return Expressions.call(CidrMatchImplementor.class, "cidrMatch", translatedOperands);
48+
}
49+
50+
public static boolean cidrMatch(ExprIpValue ip, String cidr) {
51+
ExprValue cidrValue = ExprValueUtils.stringValue(cidr);
52+
return (boolean) IPFunctions.exprCidrMatch(ip, cidrValue).valueForCalcite();
53+
}
54+
55+
public static boolean cidrMatch(String ip, String cidr) {
56+
ExprIpValue ipValue = (ExprIpValue) ExprValueUtils.ipValue(ip);
57+
return cidrMatch(ipValue, cidr);
58+
}
59+
}
60+
}

core/src/main/java/org/opensearch/sql/expression/ip/IPFunctions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ private DefaultFunctionResolver cidrmatch() {
5050
* @throws SemanticCheckException if the address or range is not valid, or if they do not use the
5151
* same version (IPv4 or IPv6).
5252
*/
53-
private ExprValue exprCidrMatch(ExprValue addressExprValue, ExprValue rangeExprValue) {
53+
public static ExprValue exprCidrMatch(ExprValue addressExprValue, ExprValue rangeExprValue) {
5454

5555
IPAddress address = addressExprValue.ipValue();
5656
IPAddress range = IPUtils.toRange(rangeExprValue.stringValue());

docs/user/ppl/functions/ip.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Description
1616

1717
Usage: `cidrmatch(ip, cidr)` checks if `ip` is within the specified `cidr` range.
1818

19-
Argument type: STRING, STRING
19+
Argument type: STRING/IP, STRING
2020

2121
Return type: BOOLEAN
2222

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.calcite.standalone;
7+
8+
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_WEBLOGS;
9+
import static org.opensearch.sql.util.MatcherUtils.rows;
10+
import static org.opensearch.sql.util.MatcherUtils.schema;
11+
import static org.opensearch.sql.util.MatcherUtils.verifyDataRows;
12+
import static org.opensearch.sql.util.MatcherUtils.verifySchema;
13+
14+
import java.io.IOException;
15+
import org.json.JSONObject;
16+
import org.junit.jupiter.api.Test;
17+
18+
public class CalcitePPLIPFunctionIT extends CalcitePPLIntegTestCase {
19+
@Override
20+
public void init() throws IOException {
21+
super.init();
22+
loadIndex(Index.GEOIP);
23+
loadIndex(Index.WEBLOG);
24+
}
25+
26+
@Test
27+
public void testCidrMatch() {
28+
// No matches
29+
JSONObject resultNoMatch =
30+
executeQuery(
31+
String.format(
32+
"source=%s | where cidrmatch(host, '250.0.0.0/24') | fields host",
33+
TEST_INDEX_WEBLOGS));
34+
verifySchema(resultNoMatch, schema("host", null, "ip"));
35+
verifyDataRows(resultNoMatch);
36+
37+
// One match
38+
JSONObject resultSingleMatch =
39+
executeQuery(
40+
String.format(
41+
"source=%s | where cidrmatch(host, '0.0.0.0/24') | fields host",
42+
TEST_INDEX_WEBLOGS));
43+
verifySchema(resultSingleMatch, schema("host", null, "ip"));
44+
verifyDataRows(resultSingleMatch, rows("0.0.0.2"));
45+
46+
// Multiple matches
47+
JSONObject resultMultipleMatch =
48+
executeQuery(
49+
String.format(
50+
"source=%s | where cidrmatch(host, '1.2.3.0/24') | fields host",
51+
TEST_INDEX_WEBLOGS));
52+
verifySchema(resultMultipleMatch, schema("host", null, "ip"));
53+
verifyDataRows(resultMultipleMatch, rows("1.2.3.4"), rows("1.2.3.5"));
54+
55+
JSONObject resultIpv6 =
56+
executeQuery(
57+
String.format(
58+
"source=%s | head 1 | eval m4 = CIDRMATCH('192.169.1.5', '192.169.1.0/24'), m6 ="
59+
+ " CIDRMATCH('2003:0db8:0000:0000:0000:0000:0000:0000', '2003:db8::/32') |"
60+
+ " fields m4, m6",
61+
TEST_INDEX_WEBLOGS));
62+
verifySchema(resultIpv6, schema("m4", "boolean"), schema("m6", "boolean"));
63+
verifyDataRows(resultIpv6, rows(true, true));
64+
}
65+
}

0 commit comments

Comments
 (0)