Skip to content

Commit

Permalink
SQL: Fix issue with negative literels and parentheses (#48113)
Browse files Browse the repository at this point in the history
Previously when a numeric literal was enclosed in parentheses and then
negated, the negation was lost and the number was considered positive, e.g.:
`-(5)` was considered as `5` instead of `-5`
`- ( (1.28) )` was considered as `1.28` instead of `-1.28`

Fixes: #48009

(cherry picked from commit 4dee4bf)
  • Loading branch information
matriv committed Oct 16, 2019
1 parent bca0c8d commit 74fe2f4
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ public Literal visitInterval(IntervalContext interval) {

TimeUnit leading = visitIntervalField(interval.leading);
TimeUnit trailing = visitIntervalField(interval.trailing);

// only YEAR TO MONTH or DAY TO HOUR/MINUTE/SECOND are valid declaration
if (trailing != null) {
if (leading == TimeUnit.YEAR && trailing != TimeUnit.MONTH) {
Expand Down Expand Up @@ -862,11 +862,28 @@ private static Source minusAwareSource(SqlBaseParser.NumberContext ctx) {
if (parentCtx != null) {
if (parentCtx instanceof SqlBaseParser.NumericLiteralContext) {
parentCtx = parentCtx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.ConstantDefaultContext) {
if (parentCtx instanceof ConstantDefaultContext) {
parentCtx = parentCtx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.ValueExpressionDefaultContext) {
if (parentCtx instanceof ValueExpressionDefaultContext) {
parentCtx = parentCtx.getParent();
if (parentCtx != null && parentCtx instanceof SqlBaseParser.ArithmeticUnaryContext) {

// Skip parentheses, e.g.: - (( (2.15) ) )
while (parentCtx instanceof PredicatedContext) {
parentCtx = parentCtx.getParent();
if (parentCtx instanceof SqlBaseParser.BooleanDefaultContext) {
parentCtx = parentCtx.getParent();
}
if (parentCtx instanceof SqlBaseParser.ExpressionContext) {
parentCtx = parentCtx.getParent();
}
if (parentCtx instanceof SqlBaseParser.ParenthesizedExpressionContext) {
parentCtx = parentCtx.getParent();
}
if (parentCtx instanceof ValueExpressionDefaultContext) {
parentCtx = parentCtx.getParent();
}
}
if (parentCtx instanceof ArithmeticUnaryContext) {
if (((ArithmeticUnaryContext) parentCtx).MINUS() != null) {
return source(parentCtx);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.StringJoiner;

import static org.elasticsearch.test.ESTestCase.randomAlphaOfLength;
import static org.elasticsearch.test.ESTestCase.randomFrom;
import static org.elasticsearch.test.ESTestCase.randomInt;
import static org.elasticsearch.test.ESTestCase.randomIntBetween;
import static org.elasticsearch.test.ESTestCase.randomNonNegativeLong;
import static org.elasticsearch.test.ESTestCase.randomZone;


public class TestUtils {
public final class TestUtils {

private TestUtils() {}

Expand All @@ -38,10 +40,10 @@ private TestUtils() {}
* that the precision of the clock can be milliseconds, microseconds or nanoseconds), whereas in Java 8
* {@code System.currentTimeMillis()} is always used. To account for these differences, this method defines a new {@code Clock}
* which will offer a value for {@code ZonedDateTime.now()} set to always have milliseconds precision.
*
*
* @return {@link ZonedDateTime} instance for the current date-time with milliseconds precision in UTC
*/
public static final ZonedDateTime now() {
public static ZonedDateTime now() {
return ZonedDateTime.now(Clock.tick(Clock.system(DateUtils.UTC), Duration.ofMillis(1)));
}

Expand Down Expand Up @@ -71,5 +73,12 @@ public static Configuration randomConfiguration(ZoneId providedZoneId) {
false);
}

public static String randomWhitespaces() {
StringJoiner sj = new StringJoiner("");
for (int i = 0; i < randomInt(10); i++) {
sj.add(randomFrom(" ", "\t", "\r", "\n"));
}
return sj.toString();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@

import java.util.List;
import java.util.Locale;
import java.util.StringJoiner;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.elasticsearch.test.TestMatchers.matchesPattern;
import static org.elasticsearch.xpack.sql.TestUtils.randomWhitespaces;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
Expand All @@ -36,14 +36,6 @@ public class EscapedFunctionsTests extends ESTestCase {

private final SqlParser parser = new SqlParser();

private String randomWhitespaces() {
StringJoiner sj = new StringJoiner("");
for (int i = 0; i < randomInt(10); i++) {
sj.add(randomFrom(" ", "\t", "\r", "\n"));
}
return sj.toString();
}

private String buildExpression(String escape, String pattern, Object value) {
return format(Locale.ROOT, "{" + randomWhitespaces() + escape + " " + randomWhitespaces() +
pattern + randomWhitespaces() + "}", value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.elasticsearch.xpack.sql.parser;

import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.TestUtils;
import org.elasticsearch.xpack.sql.expression.Expression;
import org.elasticsearch.xpack.sql.expression.Literal;
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
Expand Down Expand Up @@ -199,26 +200,55 @@ public void testNegativeLiteral() {
Expression expr = parser.createExpression("- 6");
assertEquals(Literal.class, expr.getClass());
assertEquals("- 6", expr.sourceText());
assertEquals(-6, expr.fold());

expr = parser.createExpression("- ( 6.134 )");
assertEquals(Literal.class, expr.getClass());
assertEquals("- ( 6.134 )", expr.sourceText());
assertEquals(-6.134, expr.fold());

expr = parser.createExpression("- ( ( (1.25) ) )");
assertEquals(Literal.class, expr.getClass());
assertEquals("- ( ( (1.25) ) )", expr.sourceText());
assertEquals(-1.25, expr.fold());

int numberOfParentheses = randomIntBetween(3, 10);
double value = randomDouble();
StringBuilder sb = new StringBuilder("-");
for (int i = 0; i < numberOfParentheses; i++) {
sb.append("(").append(TestUtils.randomWhitespaces());
}
sb.append(value);
for (int i = 0; i < numberOfParentheses; i++) {
sb.append(")");
if (i < numberOfParentheses - 1) {
sb.append(TestUtils.randomWhitespaces());
}
}
expr = parser.createExpression(sb.toString());
assertEquals(Literal.class, expr.getClass());
assertEquals(sb.toString(), expr.sourceText());
assertEquals(- value, expr.fold());
}

public void testComplexArithmetic() {
String sql = "-(((a-2)-(-3))+b)";
String sql = "-(((a-2)- (( - ( ( 3) )) ) )+ b)";
Expression expr = parser.createExpression(sql);
assertEquals(Neg.class, expr.getClass());
Neg neg = (Neg) expr;
assertEquals(sql, neg.sourceText());
assertEquals(1, neg.children().size());
assertEquals(Add.class, neg.children().get(0).getClass());
Add add = (Add) neg.children().get(0);
assertEquals("((a-2)-(-3))+b", add.sourceText());
assertEquals("((a-2)- (( - ( ( 3) )) ) )+ b", add.sourceText());
assertEquals(2, add.children().size());
assertEquals("?b", add.children().get(1).toString());
assertEquals(Sub.class, add.children().get(0).getClass());
Sub sub1 = (Sub) add.children().get(0);
assertEquals("(a-2)-(-3)", sub1.sourceText());
assertEquals("(a-2)- (( - ( ( 3) )) )", sub1.sourceText());
assertEquals(2, sub1.children().size());
assertEquals(Literal.class, sub1.children().get(1).getClass());
assertEquals("-3", ((Literal) sub1.children().get(1)).sourceText());
assertEquals("- ( ( 3) )", sub1.children().get(1).sourceText());
assertEquals(Sub.class, sub1.children().get(0).getClass());
Sub sub2 = (Sub) sub1.children().get(0);
assertEquals(2, sub2.children().size());
Expand Down

0 comments on commit 74fe2f4

Please sign in to comment.