Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ public LogicalPlanOptimizer(LogicalOptimizerContext optimizerContext) {
public LogicalPlan optimize(LogicalPlan verified) {
var optimized = execute(verified);

// Failures failures = verifier.verify(optimized, verified.output());
// if (failures.hasFailures()) {
// throw new VerificationException(failures);
// }
// Failures failures = verifier.verify(optimized, verified.output());
// if (failures.hasFailures()) {
// throw new VerificationException(failures);
// }
optimized.setOptimized();
return optimized;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,29 @@
package org.elasticsearch.xpack.esql.plan.logical.promql;

import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisVerificationAware;
import org.elasticsearch.xpack.esql.capabilities.TelemetryAware;
import org.elasticsearch.xpack.esql.common.Failures;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Literal;
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
import org.elasticsearch.xpack.esql.plan.logical.UnaryPlan;
import org.elasticsearch.xpack.esql.plan.logical.promql.selector.Selector;

import java.io.IOException;
import java.time.Duration;
import java.util.Objects;

import static org.elasticsearch.xpack.esql.common.Failure.fail;

/**
* Container plan for embedded PromQL queries.
* Gets eliminated by the analyzer once the query is validated.
*/
public class PromqlCommand extends UnaryPlan implements TelemetryAware {
public class PromqlCommand extends UnaryPlan implements TelemetryAware, PostAnalysisVerificationAware {

private final LogicalPlan promqlPlan;
private final Expression start, end, step;
Expand Down Expand Up @@ -102,9 +108,9 @@ public int hashCode() {
public boolean equals(Object obj) {
if (super.equals(obj)) {

PromqlCommand other = (PromqlCommand) obj;
return Objects.equals(child(), other.child()) && Objects.equals(promqlPlan, other.promqlPlan);
}
PromqlCommand other = (PromqlCommand) obj;
return Objects.equals(child(), other.child()) && Objects.equals(promqlPlan, other.promqlPlan);
}

return false;
}
Expand All @@ -123,4 +129,38 @@ public String nodeString() {
sb.append("\n<>]]");
return sb.toString();
}

@Override
public void postAnalysisVerification(Failures failures) {
promqlPlan().forEachDownMayReturnEarly((lp, breakEarly) -> {
if (lp instanceof PromqlFunctionCall fc) {
if (fc instanceof AcrossSeriesAggregate) {
breakEarly.set(true);
fc.forEachDown((childLp -> verifyNonFunctionCall(failures, childLp)));
} else if (fc instanceof WithinSeriesAggregate withinSeriesAggregate) {
failures.add(
fail(
withinSeriesAggregate,
"within time series aggregate function [{}] "
+ "can only be used inside an across time series aggregate function at this time",
withinSeriesAggregate.sourceText()
)
);
}
} else {
verifyNonFunctionCall(failures, lp);
}
});
}

private void verifyNonFunctionCall(Failures failures, LogicalPlan logicalPlan) {
if (logicalPlan instanceof Selector s) {
if (s.labelMatchers().nameLabel().matcher().isRegex()) {
failures.add(fail(s, "regex label selectors on __name__ are not supported at this time [{}]", s.sourceText()));
}
if (s.evaluation().offset() != null && s.evaluation().offset() != TimeValue.ZERO) {
failures.add(fail(s, "offset modifiers are not supported at this time [{}]", s.sourceText()));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public static Matcher from(String value) {
this.value = value;
this.isRegex = isRegex;
}

public boolean isRegex() {
return isRegex;
}
}

private final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3086,15 +3086,15 @@ private String error(String query, Object... params) {
return error(query, defaultAnalyzer, VerificationException.class, params);
}

private String error(String query, Analyzer analyzer, Object... params) {
public static String error(String query, Analyzer analyzer, Object... params) {
return error(query, analyzer, VerificationException.class, params);
}

private String error(String query, TransportVersion transportVersion, Object... params) {
return error(query, transportVersion, VerificationException.class, params);
}

private String error(String query, Analyzer analyzer, Class<? extends Exception> exception, Object... params) {
public static String error(String query, Analyzer analyzer, Class<? extends Exception> exception, Object... params) {
List<QueryParam> parameters = new ArrayList<>();
for (Object param : params) {
if (param == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.esql.analysis.promql;

import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.esql.analysis.Analyzer;
import org.elasticsearch.xpack.esql.analysis.AnalyzerTestUtils;
import org.junit.Ignore;

import java.util.List;

import static org.elasticsearch.xpack.esql.EsqlTestUtils.withDefaultLimitWarning;
import static org.elasticsearch.xpack.esql.analysis.VerifierTests.error;
import static org.hamcrest.Matchers.equalTo;

public class PromqlVerifierTests extends ESTestCase {

private final Analyzer tsdb = AnalyzerTestUtils.analyzer(AnalyzerTestUtils.tsdbIndexResolution());

public void testPromqlMissingAcrossSeriesAggregation() {
assertThat(
error("""
TS test | PROMQL step 5m (
rate(network.bytes_in[5m])
)""", tsdb),
equalTo(
"2:3: within time series aggregate function [rate(network.bytes_in[5m])] can only be used "
+ "inside an across time series aggregate function at this time"
)
);
}

public void testPromqlIllegalNameLabelMatcher() {
assertThat(
error("TS test | PROMQL step 5m ({__name__=~\"*.foo.*\"})", tsdb),
equalTo("1:27: regex label selectors on __name__ are not supported at this time [{__name__=~\"*.foo.*\"}]")
);
}

@Ignore
public void testPromqlSubquery() {
// TODO doesn't parse
// line 1:36: Invalid query 'network.bytes_in'[ValueExpressionContext] given; expected Expression but found
// InstantSelector
assertThat(error("TS test | PROMQL step 5m (avg(rate(network.bytes_in[5m:])))", tsdb), equalTo(""));
assertThat(error("TS test | PROMQL step 5m (avg(rate(network.bytes_in[5m:1m])))", tsdb), equalTo(""));
}

@Ignore
public void testPromqlArithmetricOperators() {
// TODO doesn't parse
// line 1:27: Invalid query '1+1'[ArithmeticBinaryContext] given; expected LogicalPlan but found VectorBinaryArithmetic
assertThat(
error("TS test | PROMQL step 5m (1+1)", tsdb),
equalTo("1:27: arithmetic operators are not supported at this time [foo]")
);
assertThat(
error("TS test | PROMQL step 5m (foo+1)", tsdb),
equalTo("1:27: arithmetic operators are not supported at this time [foo]")
);
assertThat(
error("TS test | PROMQL step 5m (1+foo)", tsdb),
equalTo("1:27: arithmetic operators are not supported at this time [foo]")
);
assertThat(
error("TS test | PROMQL step 5m (foo+bar)", tsdb),
equalTo("1:27: arithmetic operators are not supported at this time [foo]")
);
}

@Ignore
public void testPromqlVectorMatching() {
// TODO doesn't parse
// line 1:27: Invalid query 'method_code_http_errors_rate5m{code="500"}'[ValueExpressionContext] given; expected Expression but
// found InstantSelector
assertThat(
error(
"TS test | PROMQL step 5m (method_code_http_errors_rate5m{code=\"500\"} / ignoring(code) method_http_requests_rate5m)",
tsdb
),
equalTo("")
);
assertThat(
error(
"TS test | PROMQL step 5m (method_code_http_errors_rate5m / ignoring(code) group_left method_http_requests_rate5m)",
tsdb
),
equalTo("")
);
}

public void testPromqlModifier() {
assertThat(
error("TS test | PROMQL step 5m (foo offset 5m)", tsdb),
equalTo("1:27: offset modifiers are not supported at this time [foo offset 5m]")
);
/* TODO
assertThat(
error("TS test | PROMQL step 5m (foo @ start())", tsdb),
equalTo("1:27: @ modifiers are not supported at this time [foo @ start()]")
);*/
}

@Ignore
public void testLogicalSetBinaryOperators() {
// TODO doesn't parse
// line 1:27: Invalid query 'foo'[ValueExpressionContext] given; expected Expression but found InstantSelector
assertThat(error("TS test | PROMQL step 5m (foo and bar)", tsdb), equalTo(""));
assertThat(error("TS test | PROMQL step 5m (foo or bar)", tsdb), equalTo(""));
assertThat(error("TS test | PROMQL step 5m (foo unless bar)", tsdb), equalTo(""));
}

@Override
protected List<String> filteredWarnings() {
return withDefaultLimitWarning(super.filteredWarnings());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import org.elasticsearch.TransportVersion;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.xpack.esql.EsqlTestUtils;
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
import org.elasticsearch.xpack.esql.analysis.Analyzer;
Expand All @@ -23,7 +22,6 @@
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
import org.junit.BeforeClass;

import java.util.Collections;
import java.util.Map;

import static java.util.Collections.emptyMap;
Expand Down