Skip to content

Commit e9fb8f3

Browse files
authored
SQL: Fix handling of nested minuses (#70555) (#70601)
Previously, the parentCtx checks on a unary arithmetic expression stopped too early, resulting in wrong handling of nested minuses. Use a while loop to reach the top level unary arithmetic expression and count the number of minuses, an odd count means an acutal negation. Fixes: #66145 (cherry picked from commit db2cd3a)
1 parent d0df13a commit e9fb8f3

File tree

2 files changed

+49
-36
lines changed

2 files changed

+49
-36
lines changed

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MatchQueryContext;
9696
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MatchQueryOptionsContext;
9797
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.MultiMatchQueryContext;
98+
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NamedValueExpressionContext;
9899
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NullLiteralContext;
99100
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.NumberContext;
100101
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.OrderByContext;
@@ -856,24 +857,19 @@ public Literal visitGuidEscapedLiteral(GuidEscapedLiteralContext ctx) {
856857
*/
857858
private static Tuple<Source, String> withMinus(NumberContext ctx) {
858859
String string = ctx.getText();
859-
Source source = minusAwareSource(ctx);
860-
861-
if (source != null) {
862-
string = "-" + string;
863-
} else {
864-
source = source(ctx);
865-
}
866-
867-
return new Tuple<>(source, string);
860+
Tuple<Source, Boolean> tuple = minusAwareSource(ctx);
861+
return new Tuple<>(tuple.v1(), (tuple.v2() ? "-" : "") + string);
868862
}
869863

870864
/**
871-
* Checks the presence of MINUS (-) in the parent and if found,
872-
* returns the parent source or null otherwise.
865+
* Checks the presence of MINUS (-) in the parent and if found, returns the parent source
866+
* along with a boolean denoting if there is an odd number of MINUSes.
867+
* If not found, returns the original source.
868+
*
873869
* Parsing of the value should not depend on the returned source
874870
* as it might contain extra spaces.
875871
*/
876-
private static Source minusAwareSource(SqlBaseParser.NumberContext ctx) {
872+
private static Tuple<Source, Boolean> minusAwareSource(SqlBaseParser.NumberContext ctx) {
877873
ParserRuleContext parentCtx = ctx.getParent();
878874
if (parentCtx != null) {
879875
if (parentCtx instanceof SqlBaseParser.NumericLiteralContext) {
@@ -883,40 +879,42 @@ private static Source minusAwareSource(SqlBaseParser.NumberContext ctx) {
883879
if (parentCtx instanceof ValueExpressionDefaultContext) {
884880
parentCtx = parentCtx.getParent();
885881

886-
// Skip parentheses, e.g.: - (( (2.15) ) )
887-
while (parentCtx instanceof PredicatedContext) {
888-
parentCtx = parentCtx.getParent();
889-
if (parentCtx instanceof SqlBaseParser.BooleanDefaultContext) {
890-
parentCtx = parentCtx.getParent();
882+
ParserRuleContext returnCtx = null;
883+
boolean minus = false;
884+
while (parentCtx != null) {
885+
// Stop when we meet a higher level context to avoid unnecessary looping
886+
if (parentCtx instanceof ArithmeticBinaryContext
887+
|| parentCtx instanceof ExtractTemplateContext
888+
|| parentCtx instanceof NamedValueExpressionContext
889+
|| parentCtx instanceof PredicateContext // Not PredicatedContext !!
890+
|| parentCtx instanceof ComparisonContext) {
891+
break;
891892
}
892-
if (parentCtx instanceof SqlBaseParser.ExpressionContext) {
893-
parentCtx = parentCtx.getParent();
894-
}
895-
if (parentCtx instanceof SqlBaseParser.ParenthesizedExpressionContext) {
896-
parentCtx = parentCtx.getParent();
897-
}
898-
if (parentCtx instanceof ValueExpressionDefaultContext) {
899-
parentCtx = parentCtx.getParent();
893+
if (parentCtx instanceof ArithmeticUnaryContext) {
894+
returnCtx = parentCtx;
895+
if (((ArithmeticUnaryContext) parentCtx).MINUS() != null) {
896+
minus ^= true;
897+
}
900898
}
899+
parentCtx = parentCtx.getParent();
901900
}
902-
if (parentCtx instanceof ArithmeticUnaryContext) {
903-
if (((ArithmeticUnaryContext) parentCtx).MINUS() != null) {
904-
return source(parentCtx);
905-
}
901+
if (returnCtx != null) {
902+
return new Tuple<>(source(returnCtx), minus);
906903
}
907904
}
908905
}
909-
} else if (parentCtx instanceof SqlBaseParser.IntervalContext) {
906+
// Intervals and SysTypes can only have a single "-" as parentheses are not allowed there
907+
} else if (parentCtx instanceof IntervalContext) {
910908
IntervalContext ic = (IntervalContext) parentCtx;
911909
if (ic.sign != null && ic.sign.getType() == SqlBaseParser.MINUS) {
912-
return source(ic);
910+
return new Tuple<>(source(ic), true);
913911
}
914-
} else if (parentCtx instanceof SqlBaseParser.SysTypesContext) {
912+
} else if (parentCtx instanceof SysTypesContext) {
915913
if (((SysTypesContext) parentCtx).MINUS() != null) {
916-
return source(parentCtx);
914+
return new Tuple<>(source(parentCtx), true);
917915
}
918916
}
919917
}
920-
return null;
918+
return new Tuple<>(source(ctx), false);
921919
}
922920
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/parser/ExpressionTests.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,26 @@ public void testNegativeLiteral() {
222222
assertEquals("- ( ( (1.25) ) )", expr.sourceText());
223223
assertEquals(-1.25, expr.fold());
224224

225-
int numberOfParentheses = randomIntBetween(3, 10);
225+
expr = parser.createExpression("- ( -( (-1.25) ) )");
226+
assertEquals(Literal.class, expr.getClass());
227+
assertEquals("- ( -( (-1.25) ) )", expr.sourceText());
228+
assertEquals(-1.25, expr.fold());
229+
230+
expr = parser.createExpression("- ( -( -(-10) ) )");
231+
assertEquals(Literal.class, expr.getClass());
232+
assertEquals("- ( -( -(-10) ) )", expr.sourceText());
233+
assertEquals(10, expr.fold());
234+
235+
int numberOfParentheses = randomIntBetween(3, 20);
236+
int numberOfMinuses = 1;
226237
double value = randomDouble();
227238
StringBuilder sb = new StringBuilder("-");
228239
for (int i = 0; i < numberOfParentheses; i++) {
229240
sb.append("(").append(SqlTestUtils.randomWhitespaces());
241+
if (randomBoolean()) {
242+
sb.append("-");
243+
numberOfMinuses++;
244+
}
230245
}
231246
sb.append(value);
232247
for (int i = 0; i < numberOfParentheses; i++) {
@@ -238,7 +253,7 @@ public void testNegativeLiteral() {
238253
expr = parser.createExpression(sb.toString());
239254
assertEquals(Literal.class, expr.getClass());
240255
assertEquals(sb.toString(), expr.sourceText());
241-
assertEquals(- value, expr.fold());
256+
assertEquals(numberOfMinuses % 2 == 0 ? value : - value, expr.fold());
242257
}
243258

244259
public void testComplexArithmetic() {

0 commit comments

Comments
 (0)