Skip to content

Commit 6963c07

Browse files
committed
Consolidate grammar/ast tests
Remove GrammarTests in favor of PromqlAstTests to test not just the grammar but also the semantic validation of the queries, especially as the latter includes the former. The invalid tests are currently passing, but there are still some valid tests that are failing, likely due to incorrect AST assembly.
1 parent 377b47d commit 6963c07

File tree

5 files changed

+83
-157
lines changed

5 files changed

+83
-157
lines changed

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/predicate/operator/arithmetic/Arithmetics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public static Number mod(Number l, Number r) {
148148
return l.intValue() % r.intValue();
149149
}
150150

151-
static Number negate(Number n) {
151+
public static Number negate(Number n) {
152152
if (n == null) {
153153
return null;
154154
}

x-pack/plugin/esql/qa/testFixtures/src/main/resources/promql/grammar/queries-invalid.promql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ foo{1};
145145
|vector selector must contain at least one non-empty matcher;
146146
{x=""};
147147
|vector selector must contain at least one non-empty matcher;
148-
{x=".*"};
148+
{x=~".*"};
149149
|vector selector must contain at least one non-empty matcher;
150-
{x!".+"};
150+
{x!~".+"};
151151
|vector selector must contain at least one non-empty matcher;
152152
{x!="a"};
153153
|vector selector must contain at least one non-empty matcher;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/promql/PromqlLogicalPlanBuilder.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.xpack.esql.core.expression.Literal;
1515
import org.elasticsearch.xpack.esql.core.expression.UnresolvedAttribute;
1616
import org.elasticsearch.xpack.esql.core.expression.UnresolvedTimestamp;
17+
import org.elasticsearch.xpack.esql.core.expression.predicate.operator.arithmetic.Arithmetics;
1718
import org.elasticsearch.xpack.esql.core.tree.Node;
1819
import org.elasticsearch.xpack.esql.core.tree.Source;
1920
import org.elasticsearch.xpack.esql.core.type.DataType;
@@ -261,9 +262,36 @@ public LogicalPlan visitArithmeticUnary(PromqlBaseParser.ArithmeticUnaryContext
261262
Source source = source(ctx);
262263
LogicalPlan unary = wrapLiteral(ctx.expression());
263264

265+
// unary operators do not make sense outside numeric data
266+
if (unary instanceof LiteralSelector literalSelector) {
267+
Literal literal = literalSelector.literal();
268+
Object value = literal.value();
269+
DataType dataType = literal.dataType();
270+
if (dataType.isNumeric() == false || value instanceof Number == false) {
271+
throw new ParsingException(
272+
source,
273+
"Unary expression only allowed on expressions of type numeric or instant vector, got [{}]",
274+
dataType.typeName()
275+
);
276+
}
277+
// optimize negation in case of literals
278+
if (ctx.operator.getType() == MINUS) {
279+
Number negatedValue = Arithmetics.negate((Number) value);
280+
unary = new LiteralSelector(source, new Literal(unary.source(), negatedValue, dataType));
281+
}
282+
}
283+
// forbid range selectors
284+
else if (unary instanceof InstantSelector == false) {
285+
throw new ParsingException(
286+
source,
287+
"Unary expression only allowed on expressions of type numeric or instant vector, got [{}]",
288+
unary.nodeName()
289+
);
290+
}
291+
292+
// For non-literals (vectors), rewrite as 0 - expression
264293
if (ctx.operator.getType() == MINUS) {
265-
// TODO: optimize negation of literal
266-
LiteralSelector zero = new LiteralSelector(source, Literal.fromDouble(source, 0.0));
294+
LiteralSelector zero = new LiteralSelector(source, Literal.integer(source, 0));
267295
return new VectorBinaryArithmetic(source, zero, unary, VectorMatch.NONE, VectorBinaryArithmetic.ArithmeticOp.SUB);
268296
}
269297

@@ -311,7 +339,7 @@ public LogicalPlan visitArithmeticBinary(PromqlBaseParser.ArithmeticBinaryContex
311339
PromqlBaseParser.ModifierContext modifierCtx = ctx.modifier();
312340
if (modifierCtx != null) {
313341
// modifiers work only on vectors
314-
if (le instanceof RangeSelector || re instanceof RangeSelector) {
342+
if (le instanceof InstantSelector == false || re instanceof InstantSelector == false) {
315343
throw new ParsingException(source, "Vector matching allowed only between instant vectors");
316344
}
317345

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/promql/PromqlAstTests.java

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,39 @@
77

88
package org.elasticsearch.xpack.esql.parser.promql;
99

10+
import org.elasticsearch.common.logging.Loggers;
1011
import org.elasticsearch.core.Tuple;
12+
import org.elasticsearch.logging.LogManager;
13+
import org.elasticsearch.logging.Logger;
1114
import org.elasticsearch.test.ESTestCase;
15+
import org.elasticsearch.test.junit.annotations.TestLogging;
16+
import org.elasticsearch.xpack.esql.EsqlTestUtils;
1217
import org.elasticsearch.xpack.esql.core.QlClientException;
1318
import org.elasticsearch.xpack.esql.parser.ParsingException;
1419
import org.elasticsearch.xpack.esql.parser.PromqlParser;
1520

21+
import java.io.BufferedReader;
22+
import java.net.URL;
23+
import java.util.ArrayList;
1624
import java.util.List;
1725

1826
import static java.util.Arrays.asList;
1927
import static org.elasticsearch.common.logging.LoggerMessageFormat.format;
28+
import static org.hamcrest.Matchers.empty;
29+
import static org.hamcrest.Matchers.not;
2030

2131
/**
2232
* Test for checking the overall grammar by throwing a number of valid queries at the parser to see whether any exception is raised.
2333
* In time, the queries themselves get to be checked against the actual execution model and eventually against the expected results.
2434
*/
35+
//@TestLogging(reason = "debug", value = "org.elasticsearch.xpack.esql.parser.promql:TRACE")
2536
public class PromqlAstTests extends ESTestCase {
2637

38+
private static final Logger log = LogManager.getLogger(PromqlAstTests.class);
39+
40+
// @AwaitsFix(bugUrl = "going through them, one at a time")
2741
public void testValidQueries() throws Exception {
28-
List<Tuple<String, Integer>> lines = PromqlGrammarTests.readQueries("/promql/grammar/queries-valid.promql");
42+
List<Tuple<String, Integer>> lines = readQueries("/promql/grammar/queries-valid.promql");
2943
for (Tuple<String, Integer> line : lines) {
3044
String q = line.v1();
3145
try {
@@ -39,37 +53,53 @@ public void testValidQueries() throws Exception {
3953
}
4054
}
4155

42-
public void testQuery() throws Exception {
43-
String query = "rate(metric[5m])";
44-
new PromqlParser().createStatement(query);
45-
}
46-
47-
@AwaitsFix(bugUrl = "placeholder for individual queries")
48-
public void testSingleQuery() throws Exception {
49-
String query = "{x=\".*\"}";
50-
new PromqlParser().createStatement(query);
51-
}
52-
53-
// @AwaitsFix(bugUrl = "requires parsing validation, not the focus for now")
5456
public void testUnsupportedQueries() throws Exception {
55-
List<Tuple<String, Integer>> lines = PromqlGrammarTests.readQueries("/promql/grammar/queries-invalid.promql");
57+
List<Tuple<String, Integer>> lines = readQueries("/promql/grammar/queries-invalid.promql");
5658
for (Tuple<String, Integer> line : lines) {
5759
String q = line.v1();
5860
try {
59-
System.out.println("Testing invalid query: " + q);
61+
log.trace("Testing invalid query {}", q);
6062
PromqlParser parser = new PromqlParser();
6163
Exception pe = expectThrowsAnyOf(
6264
asList(QlClientException.class, UnsupportedOperationException.class),
6365
() -> parser.createStatement(q)
6466
);
6567
parser.createStatement(q);
66-
// System.out.printf(pe.getMessage());
68+
log.trace("{}", pe.getMessage());
6769
} catch (QlClientException | UnsupportedOperationException ex) {
6870
// Expected
6971
}
70-
// } catch (AssertionError ae) {
71-
// fail(format(null, "Unexpected exception for line {}: [{}] \n {}", line.v2(), line.v1(), ae.getCause()));
72-
// }
7372
}
7473
}
74+
75+
static List<Tuple<String, Integer>> readQueries(String source) throws Exception {
76+
var urls = EsqlTestUtils.classpathResources(source);
77+
assertThat(urls, not(empty()));
78+
List<Tuple<String, Integer>> queries = new ArrayList<>();
79+
80+
StringBuilder query = new StringBuilder();
81+
for (URL url : urls) {
82+
try (BufferedReader reader = EsqlTestUtils.reader(url)) {
83+
String line;
84+
int lineNumber = 1;
85+
86+
while ((line = reader.readLine()) != null) {
87+
// ignore comments
88+
if (line.isEmpty() == false && line.startsWith("//") == false) {
89+
query.append(line);
90+
91+
if (line.endsWith(";")) {
92+
query.setLength(query.length() - 1);
93+
queries.add(new Tuple<>(query.toString(), lineNumber));
94+
query.setLength(0);
95+
} else {
96+
query.append("\n");
97+
}
98+
}
99+
lineNumber++;
100+
}
101+
}
102+
}
103+
return queries;
104+
}
75105
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/promql/PromqlGrammarTests.java

Lines changed: 0 additions & 132 deletions
This file was deleted.

0 commit comments

Comments
 (0)