diff --git a/core/build.gradle b/core/build.gradle index f36777030c..c596251342 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -57,6 +57,7 @@ dependencies { api group: 'com.google.code.gson', name: 'gson', version: '2.8.9' api group: 'com.tdunning', name: 't-digest', version: '3.3' api project(':common') + implementation "com.github.seancfoley:ipaddress:5.4.2" testImplementation('org.junit.jupiter:junit-jupiter:5.9.3') testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: '2.1' diff --git a/core/src/main/java/org/opensearch/sql/exception/QueryEngineException.java b/core/src/main/java/org/opensearch/sql/exception/QueryEngineException.java index b3d13bef71..122d4963fa 100644 --- a/core/src/main/java/org/opensearch/sql/exception/QueryEngineException.java +++ b/core/src/main/java/org/opensearch/sql/exception/QueryEngineException.java @@ -11,4 +11,8 @@ public class QueryEngineException extends RuntimeException { public QueryEngineException(String message) { super(message); } + + public QueryEngineException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/core/src/main/java/org/opensearch/sql/exception/SemanticCheckException.java b/core/src/main/java/org/opensearch/sql/exception/SemanticCheckException.java index 6e0c184af8..c43dfdffc8 100644 --- a/core/src/main/java/org/opensearch/sql/exception/SemanticCheckException.java +++ b/core/src/main/java/org/opensearch/sql/exception/SemanticCheckException.java @@ -7,7 +7,12 @@ /** Semantic Check Exception. */ public class SemanticCheckException extends QueryEngineException { + public SemanticCheckException(String message) { super(message); } + + public SemanticCheckException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/core/src/main/java/org/opensearch/sql/expression/DSL.java b/core/src/main/java/org/opensearch/sql/expression/DSL.java index 9975afac7f..54bd35e70f 100644 --- a/core/src/main/java/org/opensearch/sql/expression/DSL.java +++ b/core/src/main/java/org/opensearch/sql/expression/DSL.java @@ -563,6 +563,10 @@ public static FunctionExpression regexp(Expression... expressions) { return compile(FunctionProperties.None, BuiltinFunctionName.REGEXP, expressions); } + public static FunctionExpression cidrmatch(Expression... expressions) { + return compile(FunctionProperties.None, BuiltinFunctionName.CIDRMATCH, expressions); + } + public static FunctionExpression concat(Expression... expressions) { return compile(FunctionProperties.None, BuiltinFunctionName.CONCAT, expressions); } diff --git a/core/src/main/java/org/opensearch/sql/expression/aggregation/AggregatorFunction.java b/core/src/main/java/org/opensearch/sql/expression/aggregation/AggregatorFunctions.java similarity index 99% rename from core/src/main/java/org/opensearch/sql/expression/aggregation/AggregatorFunction.java rename to core/src/main/java/org/opensearch/sql/expression/aggregation/AggregatorFunctions.java index 631eb2e613..698fb20408 100644 --- a/core/src/main/java/org/opensearch/sql/expression/aggregation/AggregatorFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/aggregation/AggregatorFunctions.java @@ -40,7 +40,7 @@ * count accepts values of all types. */ @UtilityClass -public class AggregatorFunction { +public class AggregatorFunctions { /** * Register Aggregation Function. * diff --git a/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java b/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunctions.java similarity index 86% rename from core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java rename to core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunctions.java index a42a599ad8..411bd27993 100644 --- a/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunctions.java @@ -101,7 +101,7 @@ */ @UtilityClass @SuppressWarnings("unchecked") -public class DateTimeFunction { +public class DateTimeFunctions { // The number of seconds per day public static final long SECONDS_PER_DAY = 86400; @@ -357,8 +357,8 @@ private DefaultFunctionResolver adddate() { BuiltinFunctionName.ADDDATE.getName(), (SerializableFunction>[]) (Stream.concat( - get_date_add_date_sub_signatures(DateTimeFunction::exprAddDateInterval), - get_adddate_subdate_signatures(DateTimeFunction::exprAddDateDays)) + get_date_add_date_sub_signatures(DateTimeFunctions::exprAddDateInterval), + get_adddate_subdate_signatures(DateTimeFunctions::exprAddDateDays)) .toArray(SerializableFunction[]::new))); } @@ -375,41 +375,41 @@ private DefaultFunctionResolver addtime() { return define( BuiltinFunctionName.ADDTIME.getName(), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), TIME, TIME, TIME), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIME, TIME, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), TIME, TIME, DATE), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIME, TIME, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIME, TIME, TIMESTAMP), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIMESTAMP, DATE, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIMESTAMP, DATE, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIMESTAMP, DATE, TIMESTAMP), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIMESTAMP, TIMESTAMP, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIMESTAMP, TIMESTAMP, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprAddTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprAddTime), TIMESTAMP, TIMESTAMP, TIMESTAMP)); @@ -425,13 +425,13 @@ private DefaultFunctionResolver convert_tz() { return define( BuiltinFunctionName.CONVERT_TZ.getName(), impl( - nullMissingHandling(DateTimeFunction::exprConvertTZ), + nullMissingHandling(DateTimeFunctions::exprConvertTZ), TIMESTAMP, TIMESTAMP, STRING, STRING), impl( - nullMissingHandling(DateTimeFunction::exprConvertTZ), + nullMissingHandling(DateTimeFunctions::exprConvertTZ), TIMESTAMP, STRING, STRING, @@ -445,9 +445,9 @@ private DefaultFunctionResolver convert_tz() { private DefaultFunctionResolver date() { return define( BuiltinFunctionName.DATE.getName(), - impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, STRING), - impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, DATE), - impl(nullMissingHandling(DateTimeFunction::exprDate), DATE, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprDate), DATE, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprDate), DATE, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprDate), DATE, TIMESTAMP)); } /** @@ -458,35 +458,35 @@ private DefaultFunctionResolver datediff() { return define( BuiltinFunctionName.DATEDIFF.getName(), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), LONG, DATE, DATE), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, DATE, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), LONG, DATE, TIME), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, DATE, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), LONG, TIME, DATE), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, TIME, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), LONG, TIME, TIME), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, TIME, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, TIMESTAMP, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, DATE, TIMESTAMP), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, TIMESTAMP, TIMESTAMP), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, TIMESTAMP, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprDateDiff), + nullMissingHandlingWithProperties(DateTimeFunctions::exprDateDiff), LONG, TIME, TIMESTAMP)); @@ -501,15 +501,15 @@ private DefaultFunctionResolver datediff() { private FunctionResolver datetime() { return define( BuiltinFunctionName.DATETIME.getName(), - impl(nullMissingHandling(DateTimeFunction::exprDateTime), TIMESTAMP, STRING, STRING), - impl(nullMissingHandling(DateTimeFunction::exprDateTimeNoTimezone), TIMESTAMP, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprDateTime), TIMESTAMP, STRING, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprDateTimeNoTimezone), TIMESTAMP, STRING)); } private DefaultFunctionResolver date_add() { return define( BuiltinFunctionName.DATE_ADD.getName(), (SerializableFunction>[]) - get_date_add_date_sub_signatures(DateTimeFunction::exprAddDateInterval) + get_date_add_date_sub_signatures(DateTimeFunctions::exprAddDateInterval) .toArray(SerializableFunction[]::new)); } @@ -517,7 +517,7 @@ private DefaultFunctionResolver date_sub() { return define( BuiltinFunctionName.DATE_SUB.getName(), (SerializableFunction>[]) - get_date_add_date_sub_signatures(DateTimeFunction::exprSubDateInterval) + get_date_add_date_sub_signatures(DateTimeFunctions::exprSubDateInterval) .toArray(SerializableFunction[]::new)); } @@ -525,9 +525,9 @@ private DefaultFunctionResolver date_sub() { private DefaultFunctionResolver day() { return define( BuiltinFunctionName.DAY.getName(), - impl(nullMissingHandling(DateTimeFunction::exprDayOfMonth), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprDayOfMonth), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprDayOfMonth), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprDayOfMonth), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfMonth), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfMonth), INTEGER, STRING)); } /** @@ -537,9 +537,9 @@ private DefaultFunctionResolver day() { private DefaultFunctionResolver dayName() { return define( BuiltinFunctionName.DAYNAME.getName(), - impl(nullMissingHandling(DateTimeFunction::exprDayName), STRING, DATE), - impl(nullMissingHandling(DateTimeFunction::exprDayName), STRING, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprDayName), STRING, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprDayName), STRING, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprDayName), STRING, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprDayName), STRING, STRING)); } /** DAYOFMONTH(STRING/DATE/TIMESTAMP). return the day of the month (1-31). */ @@ -549,12 +549,12 @@ private DefaultFunctionResolver dayOfMonth(BuiltinFunctionName name) { implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg) -> - DateTimeFunction.dayOfMonthToday(functionProperties.getQueryStartClock())), + DateTimeFunctions.dayOfMonthToday(functionProperties.getQueryStartClock())), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprDayOfMonth), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprDayOfMonth), INTEGER, STRING), - impl(nullMissingHandling(DateTimeFunction::exprDayOfMonth), INTEGER, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprDayOfMonth), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfMonth), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfMonth), INTEGER, TIMESTAMP)); } /** @@ -567,12 +567,12 @@ private DefaultFunctionResolver dayOfWeek(FunctionName name) { implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg) -> - DateTimeFunction.dayOfWeekToday(functionProperties.getQueryStartClock())), + DateTimeFunctions.dayOfWeekToday(functionProperties.getQueryStartClock())), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprDayOfWeek), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprDayOfWeek), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprDayOfWeek), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprDayOfWeek), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfWeek), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfWeek), INTEGER, STRING)); } /** DAYOFYEAR(STRING/DATE/TIMESTAMP). return the day of the year for date (1-366). */ @@ -582,111 +582,114 @@ private DefaultFunctionResolver dayOfYear(BuiltinFunctionName dayOfYear) { implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg) -> - DateTimeFunction.dayOfYearToday(functionProperties.getQueryStartClock())), + DateTimeFunctions.dayOfYearToday(functionProperties.getQueryStartClock())), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprDayOfYear), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfYear), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprDayOfYear), INTEGER, STRING)); } private DefaultFunctionResolver extract() { return define( BuiltinFunctionName.EXTRACT.getName(), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprExtractForTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprExtractForTime), LONG, STRING, TIME), - impl(nullMissingHandling(DateTimeFunction::exprExtract), LONG, STRING, DATE), - impl(nullMissingHandling(DateTimeFunction::exprExtract), LONG, STRING, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprExtract), LONG, STRING, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprExtract), LONG, STRING, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprExtract), LONG, STRING, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprExtract), LONG, STRING, STRING)); } /** FROM_DAYS(LONG). return the date value given the day number N. */ private DefaultFunctionResolver from_days() { return define( BuiltinFunctionName.FROM_DAYS.getName(), - impl(nullMissingHandling(DateTimeFunction::exprFromDays), DATE, LONG)); + impl(nullMissingHandling(DateTimeFunctions::exprFromDays), DATE, LONG)); } private FunctionResolver from_unixtime() { return define( BuiltinFunctionName.FROM_UNIXTIME.getName(), - impl(nullMissingHandling(DateTimeFunction::exprFromUnixTime), TIMESTAMP, DOUBLE), + impl(nullMissingHandling(DateTimeFunctions::exprFromUnixTime), TIMESTAMP, DOUBLE), impl( - nullMissingHandling(DateTimeFunction::exprFromUnixTimeFormat), STRING, DOUBLE, STRING)); + nullMissingHandling(DateTimeFunctions::exprFromUnixTimeFormat), + STRING, + DOUBLE, + STRING)); } private DefaultFunctionResolver get_format() { return define( BuiltinFunctionName.GET_FORMAT.getName(), - impl(nullMissingHandling(DateTimeFunction::exprGetFormat), STRING, STRING, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprGetFormat), STRING, STRING, STRING)); } /** HOUR(STRING/TIME/DATE/TIMESTAMP). return the hour value for time. */ private DefaultFunctionResolver hour(BuiltinFunctionName name) { return define( name.getName(), - impl(nullMissingHandling(DateTimeFunction::exprHour), INTEGER, STRING), - impl(nullMissingHandling(DateTimeFunction::exprHour), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprHour), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprHour), INTEGER, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprHour), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprHour), INTEGER, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprHour), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprHour), INTEGER, TIMESTAMP)); } private DefaultFunctionResolver last_day() { return define( BuiltinFunctionName.LAST_DAY.getName(), - impl(nullMissingHandling(DateTimeFunction::exprLastDay), DATE, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprLastDay), DATE, STRING), implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg) -> - DateTimeFunction.exprLastDayToday(functionProperties.getQueryStartClock())), + DateTimeFunctions.exprLastDayToday(functionProperties.getQueryStartClock())), DATE, TIME), - impl(nullMissingHandling(DateTimeFunction::exprLastDay), DATE, DATE), - impl(nullMissingHandling(DateTimeFunction::exprLastDay), DATE, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprLastDay), DATE, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprLastDay), DATE, TIMESTAMP)); } private FunctionResolver makedate() { return define( BuiltinFunctionName.MAKEDATE.getName(), - impl(nullMissingHandling(DateTimeFunction::exprMakeDate), DATE, DOUBLE, DOUBLE)); + impl(nullMissingHandling(DateTimeFunctions::exprMakeDate), DATE, DOUBLE, DOUBLE)); } private FunctionResolver maketime() { return define( BuiltinFunctionName.MAKETIME.getName(), - impl(nullMissingHandling(DateTimeFunction::exprMakeTime), TIME, DOUBLE, DOUBLE, DOUBLE)); + impl(nullMissingHandling(DateTimeFunctions::exprMakeTime), TIME, DOUBLE, DOUBLE, DOUBLE)); } /** MICROSECOND(STRING/TIME/TIMESTAMP). return the microsecond value for time. */ private DefaultFunctionResolver microsecond() { return define( BuiltinFunctionName.MICROSECOND.getName(), - impl(nullMissingHandling(DateTimeFunction::exprMicrosecond), INTEGER, STRING), - impl(nullMissingHandling(DateTimeFunction::exprMicrosecond), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprMicrosecond), INTEGER, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprMicrosecond), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprMicrosecond), INTEGER, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprMicrosecond), INTEGER, TIMESTAMP)); } /** MINUTE(STRING/TIME/TIMESTAMP). return the minute value for time. */ private DefaultFunctionResolver minute(BuiltinFunctionName name) { return define( name.getName(), - impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, STRING), - impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprMinute), INTEGER, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprMinute), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprMinute), INTEGER, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprMinute), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprMinute), INTEGER, TIMESTAMP)); } /** MINUTE(STRING/TIME/TIMESTAMP). return the minute value for time. */ private DefaultFunctionResolver minute_of_day() { return define( BuiltinFunctionName.MINUTE_OF_DAY.getName(), - impl(nullMissingHandling(DateTimeFunction::exprMinuteOfDay), INTEGER, STRING), - impl(nullMissingHandling(DateTimeFunction::exprMinuteOfDay), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprMinuteOfDay), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprMinuteOfDay), INTEGER, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprMinuteOfDay), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprMinuteOfDay), INTEGER, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprMinuteOfDay), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprMinuteOfDay), INTEGER, TIMESTAMP)); } /** MONTH(STRING/DATE/TIMESTAMP). return the month for date (1-12). */ @@ -696,21 +699,21 @@ private DefaultFunctionResolver month(BuiltinFunctionName month) { implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg) -> - DateTimeFunction.monthOfYearToday(functionProperties.getQueryStartClock())), + DateTimeFunctions.monthOfYearToday(functionProperties.getQueryStartClock())), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprMonth), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprMonth), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprMonth), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprMonth), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprMonth), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprMonth), INTEGER, STRING)); } /** MONTHNAME(STRING/DATE/TIMESTAMP). return the full name of the month for date. */ private DefaultFunctionResolver monthName() { return define( BuiltinFunctionName.MONTHNAME.getName(), - impl(nullMissingHandling(DateTimeFunction::exprMonthName), STRING, DATE), - impl(nullMissingHandling(DateTimeFunction::exprMonthName), STRING, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprMonthName), STRING, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprMonthName), STRING, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprMonthName), STRING, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprMonthName), STRING, STRING)); } /** @@ -720,7 +723,7 @@ private DefaultFunctionResolver monthName() { private DefaultFunctionResolver period_add() { return define( BuiltinFunctionName.PERIOD_ADD.getName(), - impl(nullMissingHandling(DateTimeFunction::exprPeriodAdd), INTEGER, INTEGER, INTEGER)); + impl(nullMissingHandling(DateTimeFunctions::exprPeriodAdd), INTEGER, INTEGER, INTEGER)); } /** @@ -731,35 +734,35 @@ private DefaultFunctionResolver period_add() { private DefaultFunctionResolver period_diff() { return define( BuiltinFunctionName.PERIOD_DIFF.getName(), - impl(nullMissingHandling(DateTimeFunction::exprPeriodDiff), INTEGER, INTEGER, INTEGER)); + impl(nullMissingHandling(DateTimeFunctions::exprPeriodDiff), INTEGER, INTEGER, INTEGER)); } /** QUARTER(STRING/DATE/TIMESTAMP). return the month for date (1-4). */ private DefaultFunctionResolver quarter() { return define( BuiltinFunctionName.QUARTER.getName(), - impl(nullMissingHandling(DateTimeFunction::exprQuarter), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprQuarter), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprQuarter), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprQuarter), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprQuarter), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprQuarter), INTEGER, STRING)); } private DefaultFunctionResolver sec_to_time() { return define( BuiltinFunctionName.SEC_TO_TIME.getName(), - impl((nullMissingHandling(DateTimeFunction::exprSecToTime)), TIME, INTEGER), - impl((nullMissingHandling(DateTimeFunction::exprSecToTime)), TIME, LONG), - impl((nullMissingHandling(DateTimeFunction::exprSecToTimeWithNanos)), TIME, DOUBLE), - impl((nullMissingHandling(DateTimeFunction::exprSecToTimeWithNanos)), TIME, FLOAT)); + impl((nullMissingHandling(DateTimeFunctions::exprSecToTime)), TIME, INTEGER), + impl((nullMissingHandling(DateTimeFunctions::exprSecToTime)), TIME, LONG), + impl((nullMissingHandling(DateTimeFunctions::exprSecToTimeWithNanos)), TIME, DOUBLE), + impl((nullMissingHandling(DateTimeFunctions::exprSecToTimeWithNanos)), TIME, FLOAT)); } /** SECOND(STRING/TIME/TIMESTAMP). return the second value for time. */ private DefaultFunctionResolver second(BuiltinFunctionName name) { return define( name.getName(), - impl(nullMissingHandling(DateTimeFunction::exprSecond), INTEGER, STRING), - impl(nullMissingHandling(DateTimeFunction::exprSecond), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprSecond), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprSecond), INTEGER, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprSecond), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprSecond), INTEGER, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprSecond), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprSecond), INTEGER, TIMESTAMP)); } private DefaultFunctionResolver subdate() { @@ -767,8 +770,8 @@ private DefaultFunctionResolver subdate() { BuiltinFunctionName.SUBDATE.getName(), (SerializableFunction>[]) (Stream.concat( - get_date_add_date_sub_signatures(DateTimeFunction::exprSubDateInterval), - get_adddate_subdate_signatures(DateTimeFunction::exprSubDateDays)) + get_date_add_date_sub_signatures(DateTimeFunctions::exprSubDateInterval), + get_adddate_subdate_signatures(DateTimeFunctions::exprSubDateDays)) .toArray(SerializableFunction[]::new))); } @@ -785,41 +788,41 @@ private DefaultFunctionResolver subtime() { return define( BuiltinFunctionName.SUBTIME.getName(), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), TIME, TIME, TIME), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIME, TIME, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), TIME, TIME, DATE), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIME, TIME, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIME, TIME, TIMESTAMP), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIMESTAMP, TIMESTAMP, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIMESTAMP, TIMESTAMP, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIMESTAMP, DATE, TIME), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIMESTAMP, DATE, DATE), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIMESTAMP, DATE, TIMESTAMP), implWithProperties( - nullMissingHandlingWithProperties(DateTimeFunction::exprSubTime), + nullMissingHandlingWithProperties(DateTimeFunctions::exprSubTime), TIMESTAMP, TIMESTAMP, TIMESTAMP)); @@ -835,7 +838,7 @@ private DefaultFunctionResolver str_to_date() { implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg, format) -> - DateTimeFunction.exprStrToDate(functionProperties, arg, format)), + DateTimeFunctions.exprStrToDate(functionProperties, arg, format)), TIMESTAMP, STRING, STRING)); @@ -848,10 +851,10 @@ private DefaultFunctionResolver str_to_date() { private DefaultFunctionResolver time() { return define( BuiltinFunctionName.TIME.getName(), - impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, STRING), - impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, DATE), - impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIME), - impl(nullMissingHandling(DateTimeFunction::exprTime), TIME, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprTime), TIME, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprTime), TIME, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprTime), TIME, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprTime), TIME, TIMESTAMP)); } /** @@ -867,16 +870,16 @@ private DefaultFunctionResolver time() { private DefaultFunctionResolver timediff() { return define( BuiltinFunctionName.TIMEDIFF.getName(), - impl(nullMissingHandling(DateTimeFunction::exprTimeDiff), TIME, TIME, TIME)); + impl(nullMissingHandling(DateTimeFunctions::exprTimeDiff), TIME, TIME, TIME)); } /** TIME_TO_SEC(STRING/TIME/TIMESTAMP). return the time argument, converted to seconds. */ private DefaultFunctionResolver time_to_sec() { return define( BuiltinFunctionName.TIME_TO_SEC.getName(), - impl(nullMissingHandling(DateTimeFunction::exprTimeToSec), LONG, STRING), - impl(nullMissingHandling(DateTimeFunction::exprTimeToSec), LONG, TIME), - impl(nullMissingHandling(DateTimeFunction::exprTimeToSec), LONG, TIMESTAMP)); + impl(nullMissingHandling(DateTimeFunctions::exprTimeToSec), LONG, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprTimeToSec), LONG, TIME), + impl(nullMissingHandling(DateTimeFunctions::exprTimeToSec), LONG, TIMESTAMP)); } /** @@ -914,7 +917,7 @@ private DefaultFunctionResolver timestampadd() { return define( BuiltinFunctionName.TIMESTAMPADD.getName(), impl( - nullMissingHandling(DateTimeFunction::exprTimestampAdd), + nullMissingHandling(DateTimeFunctions::exprTimestampAdd), TIMESTAMP, STRING, INTEGER, @@ -943,7 +946,7 @@ private DefaultFunctionResolver timestampdiff() { return define( BuiltinFunctionName.TIMESTAMPDIFF.getName(), impl( - nullMissingHandling(DateTimeFunction::exprTimestampDiff), + nullMissingHandling(DateTimeFunctions::exprTimestampDiff), TIMESTAMP, STRING, TIMESTAMP, @@ -962,9 +965,9 @@ private DefaultFunctionResolver timestampdiff() { private DefaultFunctionResolver to_days() { return define( BuiltinFunctionName.TO_DAYS.getName(), - impl(nullMissingHandling(DateTimeFunction::exprToDays), LONG, STRING), - impl(nullMissingHandling(DateTimeFunction::exprToDays), LONG, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprToDays), LONG, DATE)); + impl(nullMissingHandling(DateTimeFunctions::exprToDays), LONG, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprToDays), LONG, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprToDays), LONG, DATE)); } /** @@ -975,8 +978,8 @@ private DefaultFunctionResolver to_days() { private DefaultFunctionResolver to_seconds() { return define( BuiltinFunctionName.TO_SECONDS.getName(), - impl(nullMissingHandling(DateTimeFunction::exprToSeconds), LONG, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprToSecondsForIntType), LONG, LONG)); + impl(nullMissingHandling(DateTimeFunctions::exprToSeconds), LONG, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprToSecondsForIntType), LONG, LONG)); } private FunctionResolver unix_timestamp() { @@ -984,11 +987,11 @@ private FunctionResolver unix_timestamp() { BuiltinFunctionName.UNIX_TIMESTAMP.getName(), implWithProperties( functionProperties -> - DateTimeFunction.unixTimeStamp(functionProperties.getQueryStartClock()), + DateTimeFunctions.unixTimeStamp(functionProperties.getQueryStartClock()), LONG), - impl(nullMissingHandling(DateTimeFunction::unixTimeStampOf), DOUBLE, DATE), - impl(nullMissingHandling(DateTimeFunction::unixTimeStampOf), DOUBLE, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::unixTimeStampOf), DOUBLE, DOUBLE)); + impl(nullMissingHandling(DateTimeFunctions::unixTimeStampOf), DOUBLE, DATE), + impl(nullMissingHandling(DateTimeFunctions::unixTimeStampOf), DOUBLE, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::unixTimeStampOf), DOUBLE, DOUBLE)); } /** UTC_DATE(). return the current UTC Date in format yyyy-MM-dd */ @@ -1019,24 +1022,24 @@ private DefaultFunctionResolver week(BuiltinFunctionName week) { implWithProperties( nullMissingHandlingWithProperties( (functionProperties, arg) -> - DateTimeFunction.weekOfYearToday( + DateTimeFunctions.weekOfYearToday( DEFAULT_WEEK_OF_YEAR_MODE, functionProperties.getQueryStartClock())), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprWeekWithoutMode), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprWeekWithoutMode), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprWeekWithoutMode), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprWeekWithoutMode), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprWeekWithoutMode), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprWeekWithoutMode), INTEGER, STRING), implWithProperties( nullMissingHandlingWithProperties( (functionProperties, time, modeArg) -> - DateTimeFunction.weekOfYearToday( + DateTimeFunctions.weekOfYearToday( modeArg, functionProperties.getQueryStartClock())), INTEGER, TIME, INTEGER), - impl(nullMissingHandling(DateTimeFunction::exprWeek), INTEGER, DATE, INTEGER), - impl(nullMissingHandling(DateTimeFunction::exprWeek), INTEGER, TIMESTAMP, INTEGER), - impl(nullMissingHandling(DateTimeFunction::exprWeek), INTEGER, STRING, INTEGER)); + impl(nullMissingHandling(DateTimeFunctions::exprWeek), INTEGER, DATE, INTEGER), + impl(nullMissingHandling(DateTimeFunctions::exprWeek), INTEGER, TIMESTAMP, INTEGER), + impl(nullMissingHandling(DateTimeFunctions::exprWeek), INTEGER, STRING, INTEGER)); } private DefaultFunctionResolver weekday() { @@ -1050,18 +1053,18 @@ private DefaultFunctionResolver weekday() { - 1)), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprWeekday), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprWeekday), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprWeekday), INTEGER, STRING)); } /** YEAR(STRING/DATE/TIMESTAMP). return the year for date (1000-9999). */ private DefaultFunctionResolver year() { return define( BuiltinFunctionName.YEAR.getName(), - impl(nullMissingHandling(DateTimeFunction::exprYear), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprYear), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprYear), INTEGER, STRING)); + impl(nullMissingHandling(DateTimeFunctions::exprYear), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprYear), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprYear), INTEGER, STRING)); } /** YEARWEEK(DATE[,mode]). return the week number for date. */ @@ -1075,9 +1078,9 @@ private DefaultFunctionResolver yearweek() { DEFAULT_WEEK_OF_YEAR_MODE, functionProperties.getQueryStartClock())), INTEGER, TIME), - impl(nullMissingHandling(DateTimeFunction::exprYearweekWithoutMode), INTEGER, DATE), - impl(nullMissingHandling(DateTimeFunction::exprYearweekWithoutMode), INTEGER, TIMESTAMP), - impl(nullMissingHandling(DateTimeFunction::exprYearweekWithoutMode), INTEGER, STRING), + impl(nullMissingHandling(DateTimeFunctions::exprYearweekWithoutMode), INTEGER, DATE), + impl(nullMissingHandling(DateTimeFunctions::exprYearweekWithoutMode), INTEGER, TIMESTAMP), + impl(nullMissingHandling(DateTimeFunctions::exprYearweekWithoutMode), INTEGER, STRING), implWithProperties( nullMissingHandlingWithProperties( (functionProperties, time, modeArg) -> @@ -1085,9 +1088,9 @@ private DefaultFunctionResolver yearweek() { INTEGER, TIME, INTEGER), - impl(nullMissingHandling(DateTimeFunction::exprYearweek), INTEGER, DATE, INTEGER), - impl(nullMissingHandling(DateTimeFunction::exprYearweek), INTEGER, TIMESTAMP, INTEGER), - impl(nullMissingHandling(DateTimeFunction::exprYearweek), INTEGER, STRING, INTEGER)); + impl(nullMissingHandling(DateTimeFunctions::exprYearweek), INTEGER, DATE, INTEGER), + impl(nullMissingHandling(DateTimeFunctions::exprYearweek), INTEGER, TIMESTAMP, INTEGER), + impl(nullMissingHandling(DateTimeFunctions::exprYearweek), INTEGER, STRING, INTEGER)); } /** diff --git a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java index fd5ea14a2e..a67308c96a 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java @@ -132,6 +132,9 @@ public enum BuiltinFunctionName { /** Text Functions. */ TOSTRING(FunctionName.of("tostring")), + /** IP Functions. */ + CIDRMATCH(FunctionName.of("cidrmatch")), + /** Arithmetic Operators. */ ADD(FunctionName.of("+")), ADDFUNCTION(FunctionName.of("add")), diff --git a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java index 2e16d5f01f..79ea58b860 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionRepository.java @@ -24,16 +24,17 @@ import org.opensearch.sql.data.type.ExprType; import org.opensearch.sql.exception.ExpressionEvaluationException; import org.opensearch.sql.expression.Expression; -import org.opensearch.sql.expression.aggregation.AggregatorFunction; -import org.opensearch.sql.expression.datetime.DateTimeFunction; +import org.opensearch.sql.expression.aggregation.AggregatorFunctions; +import org.opensearch.sql.expression.datetime.DateTimeFunctions; import org.opensearch.sql.expression.datetime.IntervalClause; -import org.opensearch.sql.expression.operator.arthmetic.ArithmeticFunction; -import org.opensearch.sql.expression.operator.arthmetic.MathematicalFunction; -import org.opensearch.sql.expression.operator.convert.TypeCastOperator; -import org.opensearch.sql.expression.operator.predicate.BinaryPredicateOperator; -import org.opensearch.sql.expression.operator.predicate.UnaryPredicateOperator; +import org.opensearch.sql.expression.ip.IPFunctions; +import org.opensearch.sql.expression.operator.arthmetic.ArithmeticFunctions; +import org.opensearch.sql.expression.operator.arthmetic.MathematicalFunctions; +import org.opensearch.sql.expression.operator.convert.TypeCastOperators; +import org.opensearch.sql.expression.operator.predicate.BinaryPredicateOperators; +import org.opensearch.sql.expression.operator.predicate.UnaryPredicateOperators; import org.opensearch.sql.expression.system.SystemFunctions; -import org.opensearch.sql.expression.text.TextFunction; +import org.opensearch.sql.expression.text.TextFunctions; import org.opensearch.sql.expression.window.WindowFunctions; import org.opensearch.sql.storage.StorageEngine; @@ -69,18 +70,19 @@ public static synchronized BuiltinFunctionRepository getInstance() { instance = new BuiltinFunctionRepository(new HashMap<>()); // Register all built-in functions - ArithmeticFunction.register(instance); - BinaryPredicateOperator.register(instance); - MathematicalFunction.register(instance); - UnaryPredicateOperator.register(instance); - AggregatorFunction.register(instance); - DateTimeFunction.register(instance); + ArithmeticFunctions.register(instance); + BinaryPredicateOperators.register(instance); + MathematicalFunctions.register(instance); + UnaryPredicateOperators.register(instance); + AggregatorFunctions.register(instance); + DateTimeFunctions.register(instance); IntervalClause.register(instance); WindowFunctions.register(instance); - TextFunction.register(instance); - TypeCastOperator.register(instance); + TextFunctions.register(instance); + TypeCastOperators.register(instance); SystemFunctions.register(instance); OpenSearchFunctions.register(instance); + IPFunctions.register(instance); } return instance; } diff --git a/core/src/main/java/org/opensearch/sql/expression/function/DefaultFunctionResolver.java b/core/src/main/java/org/opensearch/sql/expression/function/DefaultFunctionResolver.java index 5d0f31594b..e1d0052723 100644 --- a/core/src/main/java/org/opensearch/sql/expression/function/DefaultFunctionResolver.java +++ b/core/src/main/java/org/opensearch/sql/expression/function/DefaultFunctionResolver.java @@ -61,7 +61,7 @@ public Pair resolve(FunctionSignature unreso && !FunctionSignature.isVarArgFunction(bestMatchEntry.getValue().getParamTypeList())) { throw new ExpressionEvaluationException( String.format( - "%s function expected %s, but get %s", + "%s function expected %s, but got %s", functionName, formatFunctions(functionBundle.keySet()), unresolvedSignature.formatTypes())); diff --git a/core/src/main/java/org/opensearch/sql/expression/ip/IPFunctions.java b/core/src/main/java/org/opensearch/sql/expression/ip/IPFunctions.java new file mode 100644 index 0000000000..b3e7fad211 --- /dev/null +++ b/core/src/main/java/org/opensearch/sql/expression/ip/IPFunctions.java @@ -0,0 +1,105 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.ip; + +import static org.opensearch.sql.data.type.ExprCoreType.BOOLEAN; +import static org.opensearch.sql.data.type.ExprCoreType.STRING; +import static org.opensearch.sql.expression.function.FunctionDSL.define; +import static org.opensearch.sql.expression.function.FunctionDSL.impl; +import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling; + +import inet.ipaddr.AddressStringException; +import inet.ipaddr.IPAddressString; +import inet.ipaddr.IPAddressStringParameters; +import lombok.experimental.UtilityClass; +import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.data.model.ExprValueUtils; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.function.BuiltinFunctionName; +import org.opensearch.sql.expression.function.BuiltinFunctionRepository; +import org.opensearch.sql.expression.function.DefaultFunctionResolver; + +/** Utility class that defines and registers IP functions. */ +@UtilityClass +public class IPFunctions { + + public void register(BuiltinFunctionRepository repository) { + repository.register(cidrmatch()); + } + + private DefaultFunctionResolver cidrmatch() { + + // TODO #3145: Add support for IP address data type. + return define( + BuiltinFunctionName.CIDRMATCH.getName(), + impl(nullMissingHandling(IPFunctions::exprCidrMatch), BOOLEAN, STRING, STRING)); + } + + /** + * Returns whether the given IP address is within the specified inclusive CIDR IP address range. + * Supports both IPv4 and IPv6 addresses. + * + * @param addressExprValue IP address as a string (e.g. "198.51.100.14" or + * "2001:0db8::ff00:42:8329"). + * @param rangeExprValue IP address range in CIDR notation as a string (e.g. "198.51.100.0/24" or + * "2001:0db8::/32") + * @return true if the address is in the range; otherwise false. + * @throws SemanticCheckException if the address or range is not valid, or if they do not use the + * same version (IPv4 or IPv6). + */ + private ExprValue exprCidrMatch(ExprValue addressExprValue, ExprValue rangeExprValue) { + + // TODO #3145: Update to support IP address data type. + String addressString = addressExprValue.stringValue(); + String rangeString = rangeExprValue.stringValue(); + + final IPAddressStringParameters validationOptions = + new IPAddressStringParameters.Builder() + .allowEmpty(false) + .setEmptyAsLoopback(false) + .allow_inet_aton(false) + .allowSingleSegment(false) + .toParams(); + + // Get and validate IP address. + IPAddressString address = + new IPAddressString(addressExprValue.stringValue(), validationOptions); + + try { + address.validate(); + } catch (AddressStringException e) { + String msg = + String.format( + "IP address '%s' is not valid. Error details: %s", addressString, e.getMessage()); + throw new SemanticCheckException(msg, e); + } + + // Get and validate CIDR IP address range. + IPAddressString range = new IPAddressString(rangeExprValue.stringValue(), validationOptions); + + try { + range.validate(); + } catch (AddressStringException e) { + String msg = + String.format( + "CIDR IP address range '%s' is not valid. Error details: %s", + rangeString, e.getMessage()); + throw new SemanticCheckException(msg, e); + } + + // Address and range must use the same IP version (IPv4 or IPv6). + if (address.isIPv4() ^ range.isIPv4()) { + String msg = + String.format( + "IP address '%s' and CIDR IP address range '%s' are not compatible. Both must be" + + " either IPv4 or IPv6.", + addressString, rangeString); + throw new SemanticCheckException(msg); + } + + return ExprValueUtils.booleanValue(range.contains(address)); + } +} diff --git a/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/ArithmeticFunction.java b/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/ArithmeticFunctions.java similarity index 99% rename from core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/ArithmeticFunction.java rename to core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/ArithmeticFunctions.java index 82b91e1d34..164de6d74c 100644 --- a/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/ArithmeticFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/ArithmeticFunctions.java @@ -37,7 +37,7 @@ * module, Accepts two numbers and produces a number. */ @UtilityClass -public class ArithmeticFunction { +public class ArithmeticFunctions { /** * Register Arithmetic Function. * diff --git a/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/MathematicalFunction.java b/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/MathematicalFunctions.java similarity index 99% rename from core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/MathematicalFunction.java rename to core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/MathematicalFunctions.java index 22f4b76573..102834f60d 100644 --- a/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/MathematicalFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/operator/arthmetic/MathematicalFunctions.java @@ -46,7 +46,7 @@ import org.opensearch.sql.expression.function.SerializableFunction; @UtilityClass -public class MathematicalFunction { +public class MathematicalFunctions { /** * Register Mathematical Functions. * diff --git a/core/src/main/java/org/opensearch/sql/expression/operator/convert/TypeCastOperator.java b/core/src/main/java/org/opensearch/sql/expression/operator/convert/TypeCastOperators.java similarity index 99% rename from core/src/main/java/org/opensearch/sql/expression/operator/convert/TypeCastOperator.java rename to core/src/main/java/org/opensearch/sql/expression/operator/convert/TypeCastOperators.java index db4b29f3b9..55e223d94c 100644 --- a/core/src/main/java/org/opensearch/sql/expression/operator/convert/TypeCastOperator.java +++ b/core/src/main/java/org/opensearch/sql/expression/operator/convert/TypeCastOperators.java @@ -42,7 +42,8 @@ import org.opensearch.sql.expression.function.FunctionDSL; @UtilityClass -public class TypeCastOperator { +public class TypeCastOperators { + /** Register Type Cast Operator. */ public static void register(BuiltinFunctionRepository repository) { repository.register(castToString()); diff --git a/core/src/main/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperator.java b/core/src/main/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperators.java similarity index 98% rename from core/src/main/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperator.java rename to core/src/main/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperators.java index bf6b3c22f5..96ff7785b7 100644 --- a/core/src/main/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperator.java +++ b/core/src/main/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperators.java @@ -36,7 +36,7 @@ * equalTo, Compare the left expression and right expression and produces a Boolean. */ @UtilityClass -public class BinaryPredicateOperator { +public class BinaryPredicateOperators { /** * Register Binary Predicate Function. * @@ -401,7 +401,7 @@ private static DefaultFunctionResolver notLike() { BuiltinFunctionName.NOT_LIKE.getName(), impl( nullMissingHandling( - (v1, v2) -> UnaryPredicateOperator.not(OperatorUtils.matches(v1, v2))), + (v1, v2) -> UnaryPredicateOperators.not(OperatorUtils.matches(v1, v2))), BOOLEAN, STRING, STRING)); diff --git a/core/src/main/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperator.java b/core/src/main/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperators.java similarity index 83% rename from core/src/main/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperator.java rename to core/src/main/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperators.java index ad9d9ac934..07bb5b2299 100644 --- a/core/src/main/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperator.java +++ b/core/src/main/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperators.java @@ -30,7 +30,8 @@ * The definition of unary predicate function not, Accepts one Boolean value and produces a Boolean. */ @UtilityClass -public class UnaryPredicateOperator { +public class UnaryPredicateOperators { + /** Register Unary Predicate Function. */ public static void register(BuiltinFunctionRepository repository) { repository.register(not()); @@ -45,7 +46,7 @@ public static void register(BuiltinFunctionRepository repository) { private static DefaultFunctionResolver not() { return FunctionDSL.define( BuiltinFunctionName.NOT.getName(), - FunctionDSL.impl(UnaryPredicateOperator::not, BOOLEAN, BOOLEAN)); + FunctionDSL.impl(UnaryPredicateOperators::not, BOOLEAN, BOOLEAN)); } /** @@ -108,11 +109,10 @@ private static DefaultFunctionResolver ifFunction() { org.apache.commons.lang3.tuple.Pair>> functionsOne = typeList.stream() - .map(v -> impl((UnaryPredicateOperator::exprIf), v, BOOLEAN, v, v)) + .map(v -> impl((UnaryPredicateOperators::exprIf), v, BOOLEAN, v, v)) .collect(Collectors.toList()); - DefaultFunctionResolver functionResolver = FunctionDSL.define(functionName, functionsOne); - return functionResolver; + return FunctionDSL.define(functionName, functionsOne); } private static DefaultFunctionResolver ifNull() { @@ -125,31 +125,28 @@ private static DefaultFunctionResolver ifNull() { org.apache.commons.lang3.tuple.Pair>> functionsOne = typeList.stream() - .map(v -> impl((UnaryPredicateOperator::exprIfNull), v, v, v)) + .map(v -> impl((UnaryPredicateOperators::exprIfNull), v, v, v)) .collect(Collectors.toList()); - DefaultFunctionResolver functionResolver = FunctionDSL.define(functionName, functionsOne); - return functionResolver; + return FunctionDSL.define(functionName, functionsOne); } private static DefaultFunctionResolver nullIf() { FunctionName functionName = BuiltinFunctionName.NULLIF.getName(); List typeList = ExprCoreType.coreTypes(); - DefaultFunctionResolver functionResolver = - FunctionDSL.define( - functionName, - typeList.stream() - .map(v -> impl((UnaryPredicateOperator::exprNullIf), v, v, v)) - .collect(Collectors.toList())); - return functionResolver; + return FunctionDSL.define( + functionName, + typeList.stream() + .map(v -> impl((UnaryPredicateOperators::exprNullIf), v, v, v)) + .collect(Collectors.toList())); } /** * v2 if v1 is null. * - * @param v1 varable 1 - * @param v2 varable 2 + * @param v1 variable 1 + * @param v2 variable 2 * @return v2 if v1 is null */ public static ExprValue exprIfNull(ExprValue v1, ExprValue v2) { @@ -157,11 +154,11 @@ public static ExprValue exprIfNull(ExprValue v1, ExprValue v2) { } /** - * return null if v1 equls to v2. + * return null if v1 equals to v2. * - * @param v1 varable 1 - * @param v2 varable 2 - * @return null if v1 equls to v2 + * @param v1 variable 1 + * @param v2 variable 2 + * @return null if v1 equals to v2 */ public static ExprValue exprNullIf(ExprValue v1, ExprValue v2) { return v1.equals(v2) ? LITERAL_NULL : v1; diff --git a/core/src/main/java/org/opensearch/sql/expression/text/TextFunction.java b/core/src/main/java/org/opensearch/sql/expression/text/TextFunctions.java similarity index 95% rename from core/src/main/java/org/opensearch/sql/expression/text/TextFunction.java rename to core/src/main/java/org/opensearch/sql/expression/text/TextFunctions.java index d670843551..8a5302070c 100644 --- a/core/src/main/java/org/opensearch/sql/expression/text/TextFunction.java +++ b/core/src/main/java/org/opensearch/sql/expression/text/TextFunctions.java @@ -38,7 +38,7 @@ * implementation should rely on ExprValue. */ @UtilityClass -public class TextFunction { +public class TextFunctions { private static String EMPTY_STRING = ""; /** @@ -76,9 +76,9 @@ public void register(BuiltinFunctionRepository repository) { private DefaultFunctionResolver substringSubstr(FunctionName functionName) { return define( functionName, - impl(nullMissingHandling(TextFunction::exprSubstrStart), STRING, STRING, INTEGER), + impl(nullMissingHandling(TextFunctions::exprSubstrStart), STRING, STRING, INTEGER), impl( - nullMissingHandling(TextFunction::exprSubstrStartLength), + nullMissingHandling(TextFunctions::exprSubstrStartLength), STRING, STRING, INTEGER, @@ -267,7 +267,7 @@ private DefaultFunctionResolver strcmp() { private DefaultFunctionResolver right() { return define( BuiltinFunctionName.RIGHT.getName(), - impl(nullMissingHandling(TextFunction::exprRight), STRING, STRING, INTEGER)); + impl(nullMissingHandling(TextFunctions::exprRight), STRING, STRING, INTEGER)); } /** @@ -279,7 +279,7 @@ private DefaultFunctionResolver right() { private DefaultFunctionResolver left() { return define( BuiltinFunctionName.LEFT.getName(), - impl(nullMissingHandling(TextFunction::exprLeft), STRING, STRING, INTEGER)); + impl(nullMissingHandling(TextFunctions::exprLeft), STRING, STRING, INTEGER)); } /** @@ -292,7 +292,7 @@ private DefaultFunctionResolver left() { private DefaultFunctionResolver ascii() { return define( BuiltinFunctionName.ASCII.getName(), - impl(nullMissingHandling(TextFunction::exprAscii), INTEGER, STRING)); + impl(nullMissingHandling(TextFunctions::exprAscii), INTEGER, STRING)); } /** @@ -310,14 +310,15 @@ private DefaultFunctionResolver locate() { BuiltinFunctionName.LOCATE.getName(), impl( nullMissingHandling( - (SerializableBiFunction) TextFunction::exprLocate), + (SerializableBiFunction) + TextFunctions::exprLocate), INTEGER, STRING, STRING), impl( nullMissingHandling( (SerializableTriFunction) - TextFunction::exprLocate), + TextFunctions::exprLocate), INTEGER, STRING, STRING, @@ -337,7 +338,8 @@ private DefaultFunctionResolver position() { BuiltinFunctionName.POSITION.getName(), impl( nullMissingHandling( - (SerializableBiFunction) TextFunction::exprLocate), + (SerializableBiFunction) + TextFunctions::exprLocate), INTEGER, STRING, STRING)); @@ -353,7 +355,7 @@ private DefaultFunctionResolver position() { private DefaultFunctionResolver replace() { return define( BuiltinFunctionName.REPLACE.getName(), - impl(nullMissingHandling(TextFunction::exprReplace), STRING, STRING, STRING, STRING)); + impl(nullMissingHandling(TextFunctions::exprReplace), STRING, STRING, STRING, STRING)); } /** @@ -365,7 +367,7 @@ private DefaultFunctionResolver replace() { private DefaultFunctionResolver reverse() { return define( BuiltinFunctionName.REVERSE.getName(), - impl(nullMissingHandling(TextFunction::exprReverse), STRING, STRING)); + impl(nullMissingHandling(TextFunctions::exprReverse), STRING, STRING)); } private static ExprValue exprSubstrStart(ExprValue exprValue, ExprValue start) { diff --git a/core/src/main/java/org/opensearch/sql/planner/physical/FilterOperator.java b/core/src/main/java/org/opensearch/sql/planner/physical/FilterOperator.java index ec61d53163..192ea5cb4f 100644 --- a/core/src/main/java/org/opensearch/sql/planner/physical/FilterOperator.java +++ b/core/src/main/java/org/opensearch/sql/planner/physical/FilterOperator.java @@ -13,13 +13,13 @@ import lombok.ToString; import org.opensearch.sql.data.model.ExprValue; import org.opensearch.sql.expression.Expression; -import org.opensearch.sql.expression.operator.predicate.BinaryPredicateOperator; +import org.opensearch.sql.expression.operator.predicate.BinaryPredicateOperators; import org.opensearch.sql.storage.bindingtuple.BindingTuple; /** * The Filter operator represents WHERE clause and uses the conditions to evaluate the input {@link * BindingTuple}. The Filter operator only returns the results that evaluated to true. The NULL and - * MISSING are handled by the logic defined in {@link BinaryPredicateOperator}. + * MISSING are handled by the logic defined in {@link BinaryPredicateOperators}. */ @EqualsAndHashCode(callSuper = false) @ToString diff --git a/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java b/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java index 8d935b11d2..2412bd9474 100644 --- a/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java +++ b/core/src/test/java/org/opensearch/sql/analysis/AnalyzerTest.java @@ -158,7 +158,7 @@ public void filter_relation_with_invalid_qualifiedName_ExpressionEvaluationExcep "= function expected {[BYTE,BYTE],[SHORT,SHORT],[INTEGER,INTEGER],[LONG,LONG]," + "[FLOAT,FLOAT],[DOUBLE,DOUBLE],[STRING,STRING],[BOOLEAN,BOOLEAN],[DATE,DATE]," + "[TIME,TIME],[TIMESTAMP,TIMESTAMP],[INTERVAL,INTERVAL]," - + "[STRUCT,STRUCT],[ARRAY,ARRAY]}, but get [STRING,INTEGER]", + + "[STRUCT,STRUCT],[ARRAY,ARRAY]}, but got [STRING,INTEGER]", exception.getMessage()); } diff --git a/core/src/test/java/org/opensearch/sql/expression/aggregation/PercentileApproxAggregatorTest.java b/core/src/test/java/org/opensearch/sql/expression/aggregation/PercentileApproxAggregatorTest.java index ac617e7b32..33fc325204 100644 --- a/core/src/test/java/org/opensearch/sql/expression/aggregation/PercentileApproxAggregatorTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/aggregation/PercentileApproxAggregatorTest.java @@ -13,11 +13,18 @@ package org.opensearch.sql.expression.aggregation; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -import static org.opensearch.sql.data.model.ExprValueUtils.*; -import static org.opensearch.sql.data.type.ExprCoreType.*; +import static org.opensearch.sql.data.model.ExprValueUtils.integerValue; +import static org.opensearch.sql.data.model.ExprValueUtils.longValue; +import static org.opensearch.sql.data.type.ExprCoreType.DOUBLE; +import static org.opensearch.sql.data.type.ExprCoreType.FLOAT; +import static org.opensearch.sql.data.type.ExprCoreType.INTEGER; +import static org.opensearch.sql.data.type.ExprCoreType.LONG; +import static org.opensearch.sql.data.type.ExprCoreType.STRING; import java.util.ArrayList; import java.util.List; @@ -195,7 +202,7 @@ public void test_percentile_with_invalid_size() { "percentile_approx function expected" + " {[INTEGER,DOUBLE],[INTEGER,DOUBLE,DOUBLE],[LONG,DOUBLE],[LONG,DOUBLE,DOUBLE]," + "[FLOAT,DOUBLE],[FLOAT,DOUBLE,DOUBLE],[DOUBLE,DOUBLE],[DOUBLE,DOUBLE,DOUBLE]}," - + " but get [DOUBLE,STRING]", + + " but got [DOUBLE,STRING]", exception2.getMessage()); } diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/DateAddAndAddDateTest.java b/core/src/test/java/org/opensearch/sql/expression/datetime/DateAddAndAddDateTest.java index 519e97bdc6..b4ab3a8567 100644 --- a/core/src/test/java/org/opensearch/sql/expression/datetime/DateAddAndAddDateTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/DateAddAndAddDateTest.java @@ -156,7 +156,7 @@ public void adddate_has_second_signature_but_not_date_add() { () -> date_add(LocalDateTime.of(1961, 4, 12, 9, 7), 100500)); assertEquals( "date_add function expected {[DATE,INTERVAL],[TIMESTAMP,INTERVAL]," - + "[TIME,INTERVAL]}, but get [TIMESTAMP,INTEGER]", + + "[TIME,INTERVAL]}, but got [TIMESTAMP,INTEGER]", exception.getMessage()); } diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/DateSubAndSubDateTest.java b/core/src/test/java/org/opensearch/sql/expression/datetime/DateSubAndSubDateTest.java index 123ecda0bd..897f49cfee 100644 --- a/core/src/test/java/org/opensearch/sql/expression/datetime/DateSubAndSubDateTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/DateSubAndSubDateTest.java @@ -139,7 +139,7 @@ public void subdate_has_second_signature_but_not_date_sub() { ExpressionEvaluationException.class, () -> date_sub(LocalDateTime.of(1961, 4, 12, 9, 7), 100500)); assertEquals( - "date_sub function expected {[DATE,INTERVAL],[TIMESTAMP,INTERVAL],[TIME,INTERVAL]}, but get" + "date_sub function expected {[DATE,INTERVAL],[TIMESTAMP,INTERVAL],[TIME,INTERVAL]}, but got" + " [TIMESTAMP,INTEGER]", exception.getMessage()); } diff --git a/core/src/test/java/org/opensearch/sql/expression/datetime/ToSecondsTest.java b/core/src/test/java/org/opensearch/sql/expression/datetime/ToSecondsTest.java index 910fe42a52..e983eb28f6 100644 --- a/core/src/test/java/org/opensearch/sql/expression/datetime/ToSecondsTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/datetime/ToSecondsTest.java @@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.opensearch.sql.data.type.ExprCoreType.LONG; -import static org.opensearch.sql.expression.datetime.DateTimeFunction.SECONDS_PER_DAY; +import static org.opensearch.sql.expression.datetime.DateTimeFunctions.SECONDS_PER_DAY; import java.time.Duration; import java.time.LocalDate; diff --git a/core/src/test/java/org/opensearch/sql/expression/function/DefaultFunctionResolverTest.java b/core/src/test/java/org/opensearch/sql/expression/function/DefaultFunctionResolverTest.java index ad9e8a6661..0c0439a764 100644 --- a/core/src/test/java/org/opensearch/sql/expression/function/DefaultFunctionResolverTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/function/DefaultFunctionResolverTest.java @@ -72,7 +72,7 @@ void resolve_function_not_match() { assertThrows( ExpressionEvaluationException.class, () -> resolver.resolve(functionSignature)); assertEquals( - "add function expected {[INTEGER,INTEGER]}, but get [BOOLEAN,BOOLEAN]", + "add function expected {[INTEGER,INTEGER]}, but got [BOOLEAN,BOOLEAN]", exception.getMessage()); } diff --git a/core/src/test/java/org/opensearch/sql/expression/ip/IPFunctionTest.java b/core/src/test/java/org/opensearch/sql/expression/ip/IPFunctionTest.java new file mode 100644 index 0000000000..b50bf9fd1f --- /dev/null +++ b/core/src/test/java/org/opensearch/sql/expression/ip/IPFunctionTest.java @@ -0,0 +1,120 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.expression.ip; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; +import static org.opensearch.sql.data.model.ExprValueUtils.LITERAL_FALSE; +import static org.opensearch.sql.data.model.ExprValueUtils.LITERAL_TRUE; +import static org.opensearch.sql.data.type.ExprCoreType.STRING; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opensearch.sql.data.model.ExprValue; +import org.opensearch.sql.data.model.ExprValueUtils; +import org.opensearch.sql.exception.SemanticCheckException; +import org.opensearch.sql.expression.DSL; +import org.opensearch.sql.expression.Expression; +import org.opensearch.sql.expression.FunctionExpression; +import org.opensearch.sql.expression.env.Environment; + +@ExtendWith(MockitoExtension.class) +public class IPFunctionTest { + + // IP range and address constants for testing. + private static final ExprValue IPv4Range = ExprValueUtils.stringValue("198.51.100.0/24"); + private static final ExprValue IPv6Range = ExprValueUtils.stringValue("2001:0db8::/32"); + + // TODO #3145: Add tests for IP address data type. + private static final ExprValue IPv4AddressBelow = ExprValueUtils.stringValue("198.51.99.1"); + private static final ExprValue IPv4AddressWithin = ExprValueUtils.stringValue("198.51.100.1"); + private static final ExprValue IPv4AddressAbove = ExprValueUtils.stringValue("198.51.101.2"); + + private static final ExprValue IPv6AddressBelow = + ExprValueUtils.stringValue("2001:0db7::ff00:42:8329"); + private static final ExprValue IPv6AddressWithin = + ExprValueUtils.stringValue("2001:0db8::ff00:42:8329"); + private static final ExprValue IPv6AddressAbove = + ExprValueUtils.stringValue("2001:0db9::ff00:42:8329"); + + // Mock value environment for testing. + @Mock private Environment env; + + @Test + public void cidrmatch_invalid_address() { + SemanticCheckException exception = + assertThrows( + SemanticCheckException.class, + () -> execute(ExprValueUtils.stringValue("INVALID"), IPv4Range)); + assertTrue( + exception.getMessage().matches("IP address 'INVALID' is not valid. Error details: .*")); + } + + @Test + public void cidrmatch_invalid_range() { + SemanticCheckException exception = + assertThrows( + SemanticCheckException.class, + () -> execute(IPv4AddressWithin, ExprValueUtils.stringValue("INVALID"))); + assertTrue( + exception + .getMessage() + .matches("CIDR IP address range 'INVALID' is not valid. Error details: .*")); + } + + @Test + public void cidrmatch_different_versions() { + SemanticCheckException exception; + + exception = + assertThrows(SemanticCheckException.class, () -> execute(IPv4AddressWithin, IPv6Range)); + assertEquals( + "IP address '198.51.100.1' and CIDR IP address range '2001:0db8::/32' are not compatible." + + " Both must be either IPv4 or IPv6.", + exception.getMessage()); + + exception = + assertThrows(SemanticCheckException.class, () -> execute(IPv6AddressWithin, IPv4Range)); + assertEquals( + "IP address '2001:0db8::ff00:42:8329' and CIDR IP address range '198.51.100.0/24' are not" + + " compatible. Both must be either IPv4 or IPv6.", + exception.getMessage()); + } + + @Test + public void cidrmatch_valid_ipv4() { + assertEquals(LITERAL_FALSE, execute(IPv4AddressBelow, IPv4Range)); + assertEquals(LITERAL_TRUE, execute(IPv4AddressWithin, IPv4Range)); + assertEquals(LITERAL_FALSE, execute(IPv4AddressAbove, IPv4Range)); + } + + @Test + public void cidrmatch_valid_ipv6() { + assertEquals(LITERAL_FALSE, execute(IPv6AddressBelow, IPv6Range)); + assertEquals(LITERAL_TRUE, execute(IPv6AddressWithin, IPv6Range)); + assertEquals(LITERAL_FALSE, execute(IPv6AddressAbove, IPv6Range)); + } + + /** + * Builds and evaluates a CIDR function expression with the given field and range expression + * values, and returns the resulting value. + */ + private ExprValue execute(ExprValue field, ExprValue range) { + + final String fieldName = "ip_address"; + FunctionExpression exp = DSL.cidrmatch(DSL.ref(fieldName, STRING), DSL.literal(range)); + + // Mock the value environment to return the specified field + // expression as the value for the "ip_address" field. + when(DSL.ref(fieldName, STRING).valueOf(env)).thenReturn(field); + + return exp.valueOf(env); + } +} diff --git a/core/src/test/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java b/core/src/test/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java index 55dfbd35c2..19cbb4674e 100644 --- a/core/src/test/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/operator/predicate/BinaryPredicateOperatorTest.java @@ -584,7 +584,7 @@ void testRegexpString(StringPatternPair stringPatternPair) { assertEquals(stringPatternPair.regExpTest(), expression.valueOf(valueEnv()).integerValue()); } - /** Todo. remove this test cases after script serilization implemented. */ + /** Todo. remove this test cases after script serialization implemented. */ @Test public void serializationTest() throws Exception { Expression expression = DSL.equal(DSL.literal("v1"), DSL.literal("v2")); diff --git a/core/src/test/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperatorTest.java b/core/src/test/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperatorTest.java index f7a1a7008a..7de4f456c9 100644 --- a/core/src/test/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperatorTest.java +++ b/core/src/test/java/org/opensearch/sql/expression/operator/predicate/UnaryPredicateOperatorTest.java @@ -221,12 +221,12 @@ public void test_if_predicate(Expression v1, Expression v2, Expression v3, Expre @ParameterizedTest @MethodSource("exprIfNullArguments") public void test_exprIfNull_predicate(ExprValue v1, ExprValue v2, ExprValue expected) { - assertEquals(expected.value(), UnaryPredicateOperator.exprIfNull(v1, v2).value()); + assertEquals(expected.value(), UnaryPredicateOperators.exprIfNull(v1, v2).value()); } @ParameterizedTest @MethodSource("exprNullIfArguments") public void test_exprNullIf_predicate(ExprValue v1, ExprValue v2, ExprValue expected) { - assertEquals(expected.value(), UnaryPredicateOperator.exprNullIf(v1, v2).value()); + assertEquals(expected.value(), UnaryPredicateOperators.exprNullIf(v1, v2).value()); } } diff --git a/docs/category.json b/docs/category.json index e90c674a2e..ca3d345e8b 100644 --- a/docs/category.json +++ b/docs/category.json @@ -28,12 +28,13 @@ "user/ppl/cmd/where.rst", "user/ppl/general/identifiers.rst", "user/ppl/general/datatypes.rst", - "user/ppl/functions/math.rst", - "user/ppl/functions/datetime.rst", - "user/ppl/functions/string.rst", "user/ppl/functions/condition.rst", + "user/ppl/functions/datetime.rst", + "user/ppl/functions/expressions.rst", + "user/ppl/functions/ip.rst", + "user/ppl/functions/math.rst", "user/ppl/functions/relevance.rst", - "user/ppl/functions/expressions.rst" + "user/ppl/functions/string.rst" ], "sql_cli": [ "user/dql/expressions.rst", diff --git a/docs/user/dql/metadata.rst b/docs/user/dql/metadata.rst index fa233020a3..aba4eb0c75 100644 --- a/docs/user/dql/metadata.rst +++ b/docs/user/dql/metadata.rst @@ -35,7 +35,7 @@ Example 1: Show All Indices Information SQL query:: os> SHOW TABLES LIKE '%' - fetched rows / total rows = 9/9 + fetched rows / total rows = 10/10 +----------------+-------------+-----------------+------------+---------+----------+------------+-----------+---------------------------+----------------+ | TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_CAT | TYPE_SCHEM | TYPE_NAME | SELF_REFERENCING_COL_NAME | REF_GENERATION | |----------------+-------------+-----------------+------------+---------+----------+------------+-----------+---------------------------+----------------| @@ -47,6 +47,7 @@ SQL query:: | docTestCluster | null | nested | BASE TABLE | null | null | null | null | null | null | | docTestCluster | null | nyc_taxi | BASE TABLE | null | null | null | null | null | null | | docTestCluster | null | people | BASE TABLE | null | null | null | null | null | null | + | docTestCluster | null | weblogs | BASE TABLE | null | null | null | null | null | null | | docTestCluster | null | wildcard | BASE TABLE | null | null | null | null | null | null | +----------------+-------------+-----------------+------------+---------+----------+------------+-----------+---------------------------+----------------+ diff --git a/docs/user/ppl/functions/condition.rst b/docs/user/ppl/functions/condition.rst index 96c3e64e72..9ce130072e 100644 --- a/docs/user/ppl/functions/condition.rst +++ b/docs/user/ppl/functions/condition.rst @@ -101,7 +101,7 @@ NULLIF Description >>>>>>>>>>> -Usage: nullif(field1, field2) return null if two parameters are same, otherwiser return field1. +Usage: nullif(field1, field2) return null if two parameters are same, otherwise return field1. Argument type: all the supported data type, (NOTE : if two parameters has different type, if two parameters has different type, you will fail semantic check) @@ -152,7 +152,7 @@ IF Description >>>>>>>>>>> -Usage: if(condition, expr1, expr2) return expr1 if condition is true, otherwiser return expr2. +Usage: if(condition, expr1, expr2) return expr1 if condition is true, otherwise return expr2. Argument type: all the supported data type, (NOTE : if expr1 and expr2 are different type, you will fail semantic check diff --git a/docs/user/ppl/functions/ip.rst b/docs/user/ppl/functions/ip.rst new file mode 100644 index 0000000000..3387974af5 --- /dev/null +++ b/docs/user/ppl/functions/ip.rst @@ -0,0 +1,38 @@ +==================== +IP Address Functions +==================== + +.. rubric:: Table of contents + +.. contents:: + :local: + :depth: 1 + +CIDRMATCH +--------- + +Description +>>>>>>>>>>> + +Usage: `cidrmatch(ip, cidr)` checks if `ip` is within the specified `cidr` range. + +Argument type: STRING, STRING + +Return type: BOOLEAN + +Example: + + os> source=weblogs | where cidrmatch(host, '199.120.110.0/24') | fields host + fetched rows / total rows = 1/1 + +----------------+ + | host | + |----------------| + | 199.120.110.21 | + +----------------+ + +Note: + - `ip` can be an IPv4 or an IPv6 address + - `cidr` can be an IPv4 or an IPv6 block + - `ip` and `cidr` must be either both IPv4 or both IPv6 + - `ip` and `cidr` must both be valid and non-empty/non-null + diff --git a/docs/user/ppl/index.rst b/docs/user/ppl/index.rst index 1fa981b1b7..9525874c59 100644 --- a/docs/user/ppl/index.rst +++ b/docs/user/ppl/index.rst @@ -102,6 +102,8 @@ The query start with search command and then flowing a set of command delimited - `System Functions `_ + - `IP Address Functions `_ + * **Optimization** - `Optimization <../../user/optimization/optimization.rst>`_ diff --git a/doctest/test_data/weblogs.json b/doctest/test_data/weblogs.json new file mode 100644 index 0000000000..4228e9c4d2 --- /dev/null +++ b/doctest/test_data/weblogs.json @@ -0,0 +1,6 @@ +{"index":{}} +{"host": "199.72.81.55", "method": "GET", "url": "/history/apollo/", "response": "200", "bytes": "6245"} +{"index":{}} +{"host": "199.120.110.21", "method": "GET", "url": "/shuttle/missions/sts-73/mission-sts-73.html", "response": "200", "bytes": "4085"} +{"index":{}} +{"host": "205.212.115.106", "method": "GET", "url": "/shuttle/countdown/countdown.html", "response": "200", "bytes": "3985"} diff --git a/doctest/test_docs.py b/doctest/test_docs.py index 881078a9bd..1d46766c6d 100644 --- a/doctest/test_docs.py +++ b/doctest/test_docs.py @@ -29,7 +29,7 @@ WILDCARD = "wildcard" NESTED = "nested" DATASOURCES = ".ql-datasources" - +WEBLOGS = "weblogs" class DocTestConnection(OpenSearchConnection): @@ -122,6 +122,7 @@ def set_up_test_indices(test): load_file("wildcard.json", index_name=WILDCARD) load_file("nested_objects.json", index_name=NESTED) load_file("datasources.json", index_name=DATASOURCES) + load_file("weblogs.json", index_name=WEBLOGS) def load_file(filename, index_name): @@ -150,7 +151,7 @@ def set_up(test): def tear_down(test): # drop leftover tables after each test - test_data_client.indices.delete(index=[ACCOUNTS, EMPLOYEES, PEOPLE, ACCOUNT2, NYC_TAXI, BOOKS, APACHE, WILDCARD, NESTED], ignore_unavailable=True) + test_data_client.indices.delete(index=[ACCOUNTS, EMPLOYEES, PEOPLE, ACCOUNT2, NYC_TAXI, BOOKS, APACHE, WILDCARD, NESTED, WEBLOGS], ignore_unavailable=True) docsuite = partial(doctest.DocFileSuite, diff --git a/integ-test/src/test/java/org/opensearch/sql/legacy/JdbcTestIT.java b/integ-test/src/test/java/org/opensearch/sql/legacy/JdbcTestIT.java index 74acad4f52..005119a9bc 100644 --- a/integ-test/src/test/java/org/opensearch/sql/legacy/JdbcTestIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/legacy/JdbcTestIT.java @@ -155,7 +155,9 @@ public void dateFunctionNameCaseInsensitiveTest() { public void ipTypeShouldPassJdbcFormatter() { assertThat( executeQuery( - "SELECT host AS hostIP FROM " + TestsConstants.TEST_INDEX_WEBLOG + " ORDER BY hostIP", + "SELECT host_ip AS hostIP FROM " + + TestsConstants.TEST_INDEX_WEBLOG + + " ORDER BY hostIP", "jdbc"), containsString("\"type\": \"ip\"")); } diff --git a/integ-test/src/test/java/org/opensearch/sql/ppl/IPFunctionIT.java b/integ-test/src/test/java/org/opensearch/sql/ppl/IPFunctionIT.java new file mode 100644 index 0000000000..adb044d0d2 --- /dev/null +++ b/integ-test/src/test/java/org/opensearch/sql/ppl/IPFunctionIT.java @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.sql.ppl; + +import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_WEBLOG; +import static org.opensearch.sql.util.MatcherUtils.rows; +import static org.opensearch.sql.util.MatcherUtils.schema; +import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; +import static org.opensearch.sql.util.MatcherUtils.verifySchema; + +import java.io.IOException; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +public class IPFunctionIT extends PPLIntegTestCase { + + @Override + public void init() throws IOException { + loadIndex(Index.WEBLOG); + } + + @Test + public void test_cidrmatch() throws IOException { + + // TODO #3145: Add tests for IP address data type. + JSONObject result; + + // No matches + result = + executeQuery( + String.format( + "source=%s | where cidrmatch(host_string, '199.120.111.0/24') | fields host_string", + TEST_INDEX_WEBLOG)); + verifySchema(result, schema("host_string", null, "string")); + verifyDataRows(result); + + // One match + result = + executeQuery( + String.format( + "source=%s | where cidrmatch(host_string, '199.120.110.0/24') | fields host_string", + TEST_INDEX_WEBLOG)); + verifySchema(result, schema("host_string", null, "string")); + verifyDataRows(result, rows("199.120.110.21")); + + // Multiple matches + result = + executeQuery( + String.format( + "source=%s | where cidrmatch(host_string, '199.0.0.0/8') | fields host_string", + TEST_INDEX_WEBLOG)); + verifySchema(result, schema("host_string", null, "string")); + verifyDataRows(result, rows("199.72.81.55"), rows("199.120.110.21")); + } +} diff --git a/integ-test/src/test/resources/indexDefinitions/weblogs_index_mapping.json b/integ-test/src/test/resources/indexDefinitions/weblogs_index_mapping.json index 05b9784313..bff3e20bb9 100644 --- a/integ-test/src/test/resources/indexDefinitions/weblogs_index_mapping.json +++ b/integ-test/src/test/resources/indexDefinitions/weblogs_index_mapping.json @@ -1,9 +1,12 @@ { "mappings": { "properties": { - "host": { + "host_ip": { "type": "ip" }, + "host_string": { + "type": "keyword" + }, "method": { "type": "text" }, diff --git a/integ-test/src/test/resources/weblogs.json b/integ-test/src/test/resources/weblogs.json index 4228e9c4d2..d2e9a968f8 100644 --- a/integ-test/src/test/resources/weblogs.json +++ b/integ-test/src/test/resources/weblogs.json @@ -1,6 +1,6 @@ {"index":{}} -{"host": "199.72.81.55", "method": "GET", "url": "/history/apollo/", "response": "200", "bytes": "6245"} +{"host_ip": "199.72.81.55", "host_string": "199.72.81.55", "method": "GET", "url": "/history/apollo/", "response": "200", "bytes": "6245"} {"index":{}} -{"host": "199.120.110.21", "method": "GET", "url": "/shuttle/missions/sts-73/mission-sts-73.html", "response": "200", "bytes": "4085"} +{"host_ip": "199.120.110.21", "host_string": "199.120.110.21", "method": "GET", "url": "/shuttle/missions/sts-73/mission-sts-73.html", "response": "200", "bytes": "4085"} {"index":{}} -{"host": "205.212.115.106", "method": "GET", "url": "/shuttle/countdown/countdown.html", "response": "200", "bytes": "3985"} +{"host_ip": "205.212.115.106", "host_string": "205.212.115.106", "method": "GET", "url": "/shuttle/countdown/countdown.html", "response": "200", "bytes": "3985"} diff --git a/opensearch/src/main/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactory.java b/opensearch/src/main/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactory.java index d9e21436b7..da51c34cec 100644 --- a/opensearch/src/main/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactory.java +++ b/opensearch/src/main/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactory.java @@ -87,9 +87,7 @@ public void extendTypeMapping(Map typeMapping) { for (var field : typeMapping.keySet()) { // Prevent overwriting, because aggregation engine may be not aware // of all niceties of all types. - if (!this.typeMapping.containsKey(field)) { - this.typeMapping.put(field, typeMapping.get(field)); - } + this.typeMapping.putIfAbsent(field, typeMapping.get(field)); } } diff --git a/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprIpValueTest.java b/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprIpValueTest.java index 38a4ad3199..5ee175f304 100644 --- a/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprIpValueTest.java +++ b/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprIpValueTest.java @@ -14,27 +14,28 @@ public class OpenSearchExprIpValueTest { - private OpenSearchExprIpValue ipValue = new OpenSearchExprIpValue("192.168.0.1"); + private final String ipString = "192.168.0.1"; + private final OpenSearchExprIpValue ipValue = new OpenSearchExprIpValue(ipString); @Test - void value() { - assertEquals("192.168.0.1", ipValue.value()); + void testValue() { + assertEquals(ipString, ipValue.value()); } @Test - void type() { + void testType() { assertEquals(OpenSearchIpType.of(), ipValue.type()); } @Test - void compare() { - assertEquals(0, ipValue.compareTo(new OpenSearchExprIpValue("192.168.0.1"))); - assertEquals(ipValue, new OpenSearchExprIpValue("192.168.0.1")); + void testCompare() { + assertEquals(0, ipValue.compareTo(new OpenSearchExprIpValue(ipString))); + assertEquals(ipValue, new OpenSearchExprIpValue(ipString)); } @Test - void equal() { - assertTrue(ipValue.equal(new OpenSearchExprIpValue("192.168.0.1"))); + void testEqual() { + assertTrue(ipValue.equal(new OpenSearchExprIpValue(ipString))); } @Test diff --git a/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactoryTest.java b/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactoryTest.java index 5fd40ef6c4..d82926077e 100644 --- a/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactoryTest.java +++ b/opensearch/src/test/java/org/opensearch/sql/opensearch/data/value/OpenSearchExprValueFactoryTest.java @@ -62,6 +62,8 @@ class OpenSearchExprValueFactoryTest { + static final String fieldIp = "ipV"; + private static final Map MAPPING = new ImmutableMap.Builder() .put("byteV", OpenSearchDataType.of(BYTE)) @@ -112,14 +114,13 @@ class OpenSearchExprValueFactoryTest { "textKeywordV", OpenSearchTextType.of( Map.of("words", OpenSearchDataType.of(OpenSearchDataType.MappingType.Keyword)))) - .put("ipV", OpenSearchDataType.of(OpenSearchDataType.MappingType.Ip)) + .put(fieldIp, OpenSearchDataType.of(OpenSearchDataType.MappingType.Ip)) .put("geoV", OpenSearchDataType.of(OpenSearchDataType.MappingType.GeoPoint)) .put("binaryV", OpenSearchDataType.of(OpenSearchDataType.MappingType.Binary)) .build(); - + private static final double TOLERANCE = 1E-5; private final OpenSearchExprValueFactory exprValueFactory = new OpenSearchExprValueFactory(MAPPING, true); - private final OpenSearchExprValueFactory exprValueFactoryNoArrays = new OpenSearchExprValueFactory(MAPPING, false); @@ -660,12 +661,13 @@ public void constructArrayOfGeoPointsReturnsAll() { @Test public void constructArrayOfIPsReturnsAll() { + final String ip1 = "192.168.0.1"; + final String ip2 = "192.168.0.2"; + assertEquals( new ExprCollectionValue( - List.of( - new OpenSearchExprIpValue("192.168.0.1"), - new OpenSearchExprIpValue("192.168.0.2"))), - tupleValue("{\"ipV\":[\"192.168.0.1\",\"192.168.0.2\"]}").get("ipV")); + List.of(new OpenSearchExprIpValue(ip1), new OpenSearchExprIpValue(ip2))), + tupleValue(String.format("{\"%s\":[\"%s\",\"%s\"]}", fieldIp, ip1, ip2)).get(fieldIp)); } @Test @@ -741,13 +743,12 @@ public void constructStruct() { @Test public void constructIP() { + final String valueIp = "192.168.0.1"; assertEquals( - new OpenSearchExprIpValue("192.168.0.1"), - tupleValue("{\"ipV\":\"192.168.0.1\"}").get("ipV")); + new OpenSearchExprIpValue(valueIp), + tupleValue(String.format("{\"%s\":\"%s\"}", fieldIp, valueIp)).get(fieldIp)); } - private static final double TOLERANCE = 1E-5; - @Test public void constructGeoPoint() { final double lat = 42.60355556; diff --git a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 b/ppl/src/main/antlr/OpenSearchPPLLexer.g4 index 9f707c13cd..21cee12675 100644 --- a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenSearchPPLLexer.g4 @@ -322,6 +322,7 @@ CAST: 'CAST'; LIKE: 'LIKE'; ISNULL: 'ISNULL'; ISNOTNULL: 'ISNOTNULL'; +CIDRMATCH: 'CIDRMATCH'; // FLOWCONTROL FUNCTIONS IFNULL: 'IFNULL'; diff --git a/ppl/src/main/antlr/OpenSearchPPLParser.g4 b/ppl/src/main/antlr/OpenSearchPPLParser.g4 index 4dc223b028..54ec23dcb9 100644 --- a/ppl/src/main/antlr/OpenSearchPPLParser.g4 +++ b/ppl/src/main/antlr/OpenSearchPPLParser.g4 @@ -629,6 +629,7 @@ conditionFunctionName : LIKE | ISNULL | ISNOTNULL + | CIDRMATCH ; // flow control function return non-boolean value @@ -829,6 +830,7 @@ keywordsCanBeId | textFunctionName | mathematicalFunctionName | positionFunctionName + | conditionFunctionName // commands | SEARCH | DESCRIBE