From 76248c78d744ca97b39a23ac3247e89c94e98987 Mon Sep 17 00:00:00 2001 From: Francesco Capponi Date: Mon, 7 Jun 2021 10:55:31 -0400 Subject: [PATCH 1/3] Adding field comparison in Legacy Engine --- .../sql/legacy/MethodQueryIT.java | 27 +++++++++++++++++ .../sql/legacy/domain/Where.java | 20 ++++++++++++- .../sql/legacy/parser/SqlParser.java | 2 +- .../sql/legacy/parser/WhereParser.java | 30 +++++++++++++++---- 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java index 862363df8c..77c564337c 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.Locale; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; /** @@ -144,4 +145,30 @@ public void matchPhraseQueryTest() throws IOException { Assert.assertThat(result, containsString("{\"match_phrase\":{\"address\":{\"query\":\"671 Bristol Street\"")); } + + @Test + public void testFieldToFieldComparison() throws IOException { + Assume.assumeFalse(isNewQueryEngineEabled()); + String result = explainQuery("select * from " + TestsConstants.TEST_INDEX_ACCOUNT + + " where age > account_number"); + + Assert.assertThat(result, + containsString("{\"script\":{\"script\":{\"source\":\"doc['age'].value " + + "> doc['account_number'].value\",\"lang\":\"painless\"}")); + + } + + @Test + public void testFieldToFieldComparisonWithExtraClause() throws IOException { + Assume.assumeFalse(isNewQueryEngineEabled()); + String result = explainQuery("select * from " + TestsConstants.TEST_INDEX_ACCOUNT + + " where age > account_number AND age in (1)"); + + Assert.assertThat(result, + containsString("{\"script\":{\"script\":{\"source\":\"doc['age'].value " + + "> doc['account_number'].value\",\"lang\":\"painless\"}")); + Assert.assertThat(result, + containsString("{\"term\":{\"age\":{\"value\":1,\"boost\":1.0}}}")); + + } } diff --git a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/domain/Where.java b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/domain/Where.java index 8b7f436f76..d929ba8280 100644 --- a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/domain/Where.java +++ b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/domain/Where.java @@ -31,16 +31,34 @@ public static Where newInstance() { return new Where(CONN.AND); } + public static Where newInstance(boolean isJoin) { + return new Where(CONN.AND, isJoin); + } + private LinkedList wheres = new LinkedList<>(); protected CONN conn; + protected final boolean isJoin; public Where(String connStr) { - this.conn = CONN.valueOf(connStr.toUpperCase()); + this(connStr, false); + } + + public Where(String connStr, boolean isJoin) { + this(CONN.valueOf(connStr.toUpperCase()), isJoin); } public Where(CONN conn) { + this(conn, false); + } + + public Where(CONN conn, boolean isJoin) { this.conn = conn; + this.isJoin=isJoin; + } + + public boolean isJoin() { + return isJoin; } public void addWhere(Where where) { diff --git a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/SqlParser.java b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/SqlParser.java index d296f66b0c..44ecc36daf 100644 --- a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/SqlParser.java +++ b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/SqlParser.java @@ -448,7 +448,7 @@ private JoinSelect createBasicJoinSelectAccordingToTableSource(SQLJoinTableSourc throws SqlParseException { JoinSelect joinSelect = new JoinSelect(); if (joinTableSource.getCondition() != null) { - Where where = Where.newInstance(); + Where where = Where.newInstance(true); WhereParser whereParser = new WhereParser(this, joinTableSource.getCondition()); whereParser.parseWhere(joinTableSource.getCondition(), where); joinSelect.setConnectedWhere(where); diff --git a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java index 73814fff0e..8e67aff653 100644 --- a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java +++ b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java @@ -214,7 +214,7 @@ private void routeCond(SQLBinaryOpExpr bExpr, SQLExpr sub, Where where) throws S if (sub instanceof SQLBinaryOpExpr && !isCond((SQLBinaryOpExpr) sub)) { SQLBinaryOpExpr binarySub = (SQLBinaryOpExpr) sub; if (binarySub.getOperator().priority != bExpr.getOperator().priority) { - Where subWhere = new Where(bExpr.getOperator().name); + Where subWhere = new Where(bExpr.getOperator().name, where.isJoin()); where.addWhere(subWhere); parseWhere(binarySub, subWhere); } else { @@ -291,7 +291,7 @@ private void explainCond(String opear, SQLExpr expr, Where where) throws SqlPars condition = new Condition(Where.CONN.valueOf(opear), soExpr.getLeft().toString(), soExpr.getLeft(), soExpr.getOperator().name, parseValue(soExpr.getRight()), soExpr.getRight(), childrenType); } else { - SQLMethodInvokeExpr sqlMethodInvokeExpr = parseSQLBinaryOpExprWhoIsConditionInWhere(soExpr); + SQLMethodInvokeExpr sqlMethodInvokeExpr = parseSQLBinaryOpExprWhoIsConditionInWhere(soExpr, where.isJoin()); if (sqlMethodInvokeExpr == null) { condition = new Condition(Where.CONN.valueOf(opear), soExpr.getLeft().toString(), soExpr.getLeft(), soExpr.getOperator().name, parseValue(soExpr.getRight()), @@ -536,10 +536,20 @@ private MethodField parseSQLCastExprWithFunctionInWhere(SQLCastExpr soExpr) thro ); } - private SQLMethodInvokeExpr parseSQLBinaryOpExprWhoIsConditionInWhere(SQLBinaryOpExpr soExpr) + private SQLMethodInvokeExpr parseSQLBinaryOpExprWhoIsConditionInWhere(SQLBinaryOpExpr soExpr, + boolean join) throws SqlParseException { - if (bothSideAreNotFunction(soExpr) && bothSidesAreNotCast(soExpr)) { + if ( + // if one of the two side is a method, it has to be resolved as script + neitherSideIsFunction(soExpr) + // if one of the two side is a cast, it has to be resolved as script + && neitherSidesIsCast(soExpr) + // if both sides are field, we cannot use the QueryRange (which would be more efficient + // `field > integer` comparisons) but ES doesn't allow comparing two fields and + // we must use a SCRIPT + && (!bothSidesAreFieldIdentifier(soExpr) || join) + ) { return null; } @@ -608,14 +618,22 @@ private SQLMethodInvokeExpr parseSQLBinaryOpExprWhoIsConditionInWhere(SQLBinaryO } - private Boolean bothSideAreNotFunction(SQLBinaryOpExpr soExpr) { + private Boolean neitherSideIsFunction(SQLBinaryOpExpr soExpr) { return !(soExpr.getLeft() instanceof SQLMethodInvokeExpr || soExpr.getRight() instanceof SQLMethodInvokeExpr); } - private Boolean bothSidesAreNotCast(SQLBinaryOpExpr soExpr) { + private Boolean neitherSidesIsCast(SQLBinaryOpExpr soExpr) { return !(soExpr.getLeft() instanceof SQLCastExpr || soExpr.getRight() instanceof SQLCastExpr); } + private Boolean bothSidesAreFieldIdentifier(SQLBinaryOpExpr soExpr) { + return (soExpr.getLeft() instanceof SQLIdentifierExpr && soExpr.getRight() instanceof SQLIdentifierExpr) + // "missing" is a SQLIdentifier but not a Field Identifier. + // We might want to have a subclass for "missing" or for "fieldIdentifier" or + // change type altogether to avoid conflicts + && !"missing".equalsIgnoreCase(((SQLIdentifierExpr) soExpr.getRight()).getLowerName()); + } + private Object[] getMethodValuesWithSubQueries(SQLMethodInvokeExpr method) throws SqlParseException { List values = new ArrayList<>(); for (SQLExpr innerExpr : method.getParameters()) { From 3a934787f08f3a9bd080e6aeeb0fee09fae45f0a Mon Sep 17 00:00:00 2001 From: Francesco Capponi Date: Fri, 11 Jun 2021 23:49:10 -0400 Subject: [PATCH 2/3] Adding support for much more permissive date parsing for ExprTimestampValue creation (support of T or not T, autofill missing part with 0s) --- .../sql/data/model/ExprTimestampValue.java | 4 ++- .../sql/data/utils/ExprDateFormatters.java | 25 +++++++++++++++++-- .../sql/data/model/DateTimeValueTest.java | 8 +++--- .../value/ElasticsearchExprValueFactory.java | 16 +++--------- .../ElasticsearchExprValueFactoryTest.java | 11 ++++++-- 5 files changed, 42 insertions(+), 22 deletions(-) rename elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchDateFormatters.java => core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/utils/ExprDateFormatters.java (84%) diff --git a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprTimestampValue.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprTimestampValue.java index 0c9c506569..894a3e82ca 100644 --- a/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprTimestampValue.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/model/ExprTimestampValue.java @@ -19,6 +19,7 @@ import com.amazon.opendistroforelasticsearch.sql.data.type.ExprCoreType; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; +import com.amazon.opendistroforelasticsearch.sql.data.utils.ExprDateFormatters; import com.amazon.opendistroforelasticsearch.sql.exception.SemanticCheckException; import java.time.Instant; import java.time.LocalDate; @@ -69,7 +70,8 @@ public class ExprTimestampValue extends AbstractExprValue { */ public ExprTimestampValue(String timestamp) { try { - this.timestamp = LocalDateTime.parse(timestamp, FORMATTER_VARIABLE_MICROS) + this.timestamp = LocalDateTime.parse(timestamp, + ExprDateFormatters.TOLERANT_PARSER_DATE_TIME_FORMATTER) .atZone(ZONE) .toInstant(); } catch (DateTimeParseException e) { diff --git a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchDateFormatters.java b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/utils/ExprDateFormatters.java similarity index 84% rename from elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchDateFormatters.java rename to core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/utils/ExprDateFormatters.java index c653c063ac..818eb71375 100644 --- a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchDateFormatters.java +++ b/core/src/main/java/com/amazon/opendistroforelasticsearch/sql/data/utils/ExprDateFormatters.java @@ -15,7 +15,7 @@ * */ -package com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.value; +package com.amazon.opendistroforelasticsearch.sql.data.utils; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.HOUR_OF_DAY; @@ -37,7 +37,7 @@ * Reference org.elasticsearch.common.time.DateFormatters. */ @UtilityClass -public class ElasticsearchDateFormatters { +public class ExprDateFormatters { public static final DateTimeFormatter TIME_ZONE_FORMATTER_NO_COLON = new DateTimeFormatterBuilder() @@ -73,7 +73,16 @@ public class ElasticsearchDateFormatters { new DateTimeFormatterBuilder() .append(STRICT_YEAR_MONTH_DAY_FORMATTER) .optionalStart() + + // otherwise use space + .optionalStart() + .appendLiteral(' ') + .optionalEnd() + // optional T + .optionalStart() .appendLiteral('T') + .optionalEnd() + .optionalStart() .appendValue(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE) .optionalStart() @@ -82,13 +91,18 @@ public class ElasticsearchDateFormatters { .optionalStart() .appendLiteral(':') .appendValue(SECOND_OF_MINUTE, 2, 2, SignStyle.NOT_NEGATIVE) + + // optional millis with dot .optionalStart() .appendFraction(NANO_OF_SECOND, 1, 9, true) .optionalEnd() + + // otherwise optional millis use with comma .optionalStart() .appendLiteral(',') .appendFraction(NANO_OF_SECOND, 1, 9, false) .optionalEnd() + .optionalEnd() .optionalEnd() .optionalStart() @@ -104,4 +118,11 @@ public class ElasticsearchDateFormatters { public static final DateTimeFormatter SQL_LITERAL_DATE_TIME_FORMAT = DateTimeFormatter .ofPattern("yyyy-MM-dd HH:mm:ss"); + + public static final DateTimeFormatter TOLERANT_PARSER_DATE_TIME_FORMATTER = + new DateTimeFormatterBuilder() + .appendOptional(STRICT_DATE_OPTIONAL_TIME_FORMATTER) + .appendOptional(SQL_LITERAL_DATE_TIME_FORMAT) + .appendOptional(STRICT_HOUR_MINUTE_SECOND_FORMATTER) + .toFormatter(); } diff --git a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/DateTimeValueTest.java b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/DateTimeValueTest.java index 294c21ec52..071258ee21 100644 --- a/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/DateTimeValueTest.java +++ b/core/src/test/java/com/amazon/opendistroforelasticsearch/sql/data/model/DateTimeValueTest.java @@ -113,9 +113,9 @@ public void timeInUnsupportedFormat() { public void timestampInUnsupportedFormat() { SemanticCheckException exception = assertThrows(SemanticCheckException.class, - () -> new ExprTimestampValue("2020-07-07T01:01:01Z")); + () -> new ExprTimestampValue("2020-07-07T1:01:01Z")); assertEquals( - "timestamp:2020-07-07T01:01:01Z in unsupported format, " + "timestamp:2020-07-07T1:01:01Z in unsupported format, " + "please use yyyy-MM-dd HH:mm:ss[.SSSSSS]", exception.getMessage()); } @@ -239,9 +239,9 @@ public void datetimeWithVariableMicroPrecision() { public void timestampOverMaxMicroPrecision() { SemanticCheckException exception = assertThrows(SemanticCheckException.class, - () -> new ExprTimestampValue("2020-07-07 01:01:01.1234567")); + () -> new ExprTimestampValue("2020-07-07 01:01:01.12345678910")); assertEquals( - "timestamp:2020-07-07 01:01:01.1234567 in unsupported format, " + "timestamp:2020-07-07 01:01:01.12345678910 in unsupported format, " + "please use yyyy-MM-dd HH:mm:ss[.SSSSSS]", exception.getMessage()); } diff --git a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactory.java b/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactory.java index 2fbae486d6..b0f768ce94 100644 --- a/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactory.java +++ b/elasticsearch/src/main/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactory.java @@ -36,9 +36,6 @@ import static com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.type.ElasticsearchDataType.ES_IP; import static com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.type.ElasticsearchDataType.ES_TEXT; import static com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.type.ElasticsearchDataType.ES_TEXT_KEYWORD; -import static com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.value.ElasticsearchDateFormatters.SQL_LITERAL_DATE_TIME_FORMAT; -import static com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.value.ElasticsearchDateFormatters.STRICT_DATE_OPTIONAL_TIME_FORMATTER; -import static com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.value.ElasticsearchDateFormatters.STRICT_HOUR_MINUTE_SECOND_FORMATTER; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprBooleanValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprByteValue; @@ -57,6 +54,7 @@ import com.amazon.opendistroforelasticsearch.sql.data.model.ExprTupleValue; import com.amazon.opendistroforelasticsearch.sql.data.model.ExprValue; import com.amazon.opendistroforelasticsearch.sql.data.type.ExprType; +import com.amazon.opendistroforelasticsearch.sql.data.utils.ExprDateFormatters; import com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.utils.Content; import com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.utils.ElasticsearchJsonContent; import com.amazon.opendistroforelasticsearch.sql.elasticsearch.data.utils.ObjectContent; @@ -64,8 +62,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -87,13 +83,6 @@ public class ElasticsearchExprValueFactory { @Setter private Map typeMapping; - private static final DateTimeFormatter DATE_TIME_FORMATTER = - new DateTimeFormatterBuilder() - .appendOptional(SQL_LITERAL_DATE_TIME_FORMAT) - .appendOptional(STRICT_DATE_OPTIONAL_TIME_FORMATTER) - .appendOptional(STRICT_HOUR_MINUTE_SECOND_FORMATTER) - .toFormatter(); - private static final String TOP_PATH = ""; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @@ -185,7 +174,8 @@ private ExprValue constructTimestamp(String value) { try { return new ExprTimestampValue( // Using Elasticsearch DateFormatters for now. - DateFormatters.from(DATE_TIME_FORMATTER.parse(value)).toInstant()); + DateFormatters.from(ExprDateFormatters.TOLERANT_PARSER_DATE_TIME_FORMATTER + .parse(value)).toInstant()); } catch (DateTimeParseException e) { throw new IllegalStateException( String.format( diff --git a/elasticsearch/src/test/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactoryTest.java b/elasticsearch/src/test/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactoryTest.java index badf6afb57..a40921a64c 100644 --- a/elasticsearch/src/test/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactoryTest.java +++ b/elasticsearch/src/test/java/com/amazon/opendistroforelasticsearch/sql/elasticsearch/data/value/ElasticsearchExprValueFactoryTest.java @@ -190,6 +190,12 @@ public void constructDate() { assertEquals( new ExprTimestampValue("2015-01-01 12:10:30"), tupleValue("{\"timestampV\":\"2015-01-01 12:10:30\"}").get("timestampV")); + assertEquals( + new ExprTimestampValue("2020-08-17T19:44:00.100500"), + tupleValue("{\"timestampV\":\"2020-08-17T19:44:00.100500\"}").get("timestampV")); + assertEquals( + new ExprTimestampValue("2020-08-17 19:44:00.100500"), + tupleValue("{\"timestampV\":\"2020-08-17 19:44:00.100500\"}").get("timestampV")); assertEquals( new ExprTimestampValue(Instant.ofEpochMilli(1420070400001L)), tupleValue("{\"timestampV\":1420070400001}").get("timestampV")); @@ -221,9 +227,10 @@ public void constructDate() { public void constructDateFromUnsupportedFormatThrowException() { IllegalStateException exception = assertThrows( - IllegalStateException.class, () -> tupleValue("{\"timestampV\":\"2015-01-01 12:10\"}")); + IllegalStateException.class, () -> + tupleValue("{\"timestampV\":\"2015-01-01 1:10:10\"}")); assertEquals( - "Construct ExprTimestampValue from \"2015-01-01 12:10\" failed, " + "Construct ExprTimestampValue from \"2015-01-01 1:10:10\" failed, " + "unsupported date format.", exception.getMessage()); } From 0284f03be6c1cc8c7aae04682878ffb57929a743 Mon Sep 17 00:00:00 2001 From: Francesco Capponi Date: Sat, 19 Jun 2021 16:19:58 -0400 Subject: [PATCH 3/3] Legacy: field comparison, adding support to <> operator (behave as !=) --- .../sql/legacy/MethodQueryIT.java | 14 ++++++++++++++ .../sql/legacy/parser/WhereParser.java | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java index 77c564337c..b8a4de1756 100644 --- a/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java +++ b/integ-test/src/test/java/com/amazon/opendistroforelasticsearch/sql/legacy/MethodQueryIT.java @@ -171,4 +171,18 @@ public void testFieldToFieldComparisonWithExtraClause() throws IOException { containsString("{\"term\":{\"age\":{\"value\":1,\"boost\":1.0}}}")); } + + @Test + public void testFieldToFieldComparisonWithDifferentOperator() throws IOException { + Assume.assumeFalse(isNewQueryEngineEabled()); + String result = explainQuery("select * from " + TestsConstants.TEST_INDEX_ACCOUNT + + " where age <> account_number AND age in (1)"); + + Assert.assertThat(result, + containsString("{\"script\":{\"script\":{\"source\":\"doc['age'].value " + + "!= doc['account_number'].value\",\"lang\":\"painless\"}")); + Assert.assertThat(result, + containsString("{\"term\":{\"age\":{\"value\":1,\"boost\":1.0}}}")); + + } } diff --git a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java index 8e67aff653..f7ef5432ac 100644 --- a/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java +++ b/legacy/src/main/java/com/amazon/opendistroforelasticsearch/sql/legacy/parser/WhereParser.java @@ -610,6 +610,10 @@ && neitherSidesIsCast(soExpr) operator = "=="; } + if (operator.equals("<>")) { + operator = "!="; + } + String finalStr = v1Dec + v2Dec + v1 + " " + operator + " " + v2; SQLMethodInvokeExpr scriptMethod = new SQLMethodInvokeExpr("script", null);