Skip to content
Closed
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 @@ -57,8 +57,13 @@
import org.elasticsearch.xpack.core.esql.action.ColumnInfo;
import org.elasticsearch.xpack.esql.VerificationException;
import org.elasticsearch.xpack.esql.analysis.AnalyzerSettings;
import org.elasticsearch.xpack.esql.core.expression.Alias;
import org.elasticsearch.xpack.esql.core.expression.Literal;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.parser.ParsingException;
import org.elasticsearch.xpack.esql.plan.EsqlStatement;
import org.elasticsearch.xpack.esql.plan.logical.Row;
import org.elasticsearch.xpack.esql.planner.PlannerSettings;
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
import org.junit.Before;
Expand Down Expand Up @@ -150,6 +155,22 @@ public void testRow() {
}
}

/**
* Verifies that a pre-built {@link EsqlStatement} supplied via
* {@link EsqlQueryRequest#syncEsqlQueryRequestWithPlan} is executed
* without going through ES|QL string parsing.
*/
public void testRowWithParsedStatement() {
var plan = new Row(Source.EMPTY, List.of(new Alias(Source.EMPTY, "x", new Literal(Source.EMPTY, 1, DataType.INTEGER))));
var statement = new EsqlStatement(plan, List.of());
EsqlQueryRequest request = EsqlQueryRequest.syncEsqlQueryRequestWithPlan(statement);
request.pragmas(getPragmas());
try (EsqlQueryResponse response = run(request)) {
assertThat(response.columns(), equalTo(List.of(new ColumnInfoImpl("x", "integer", null))));
assertEquals(List.of(List.of(1)), getValuesList(response));
}
}

public void testRowWithFilter() {
long value = randomLongBetween(0, Long.MAX_VALUE);
try (EsqlQueryResponse response = run(syncEsqlQueryRequest("ROW " + value).filter(randomQueryFilter()))) {
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugin/esql/src/main/antlr/parser/Promql.g4
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
parser grammar Promql;

promqlCommand
: PROMQL promqlParam* (valueName ASSIGN)? LP promqlQueryPart+ RP
: PROMQL promqlParam* (valueName ASSIGN)? LP NAMED_OR_POSITIONAL_PARAM RP // ?param as the entire PromQL expression (parenthesized)
| PROMQL promqlParam* (valueName ASSIGN)? NAMED_OR_POSITIONAL_PARAM // ?param as the entire PromQL expression (unparenthesized)
| PROMQL promqlParam* (valueName ASSIGN)? LP promqlQueryPart+ RP
| PROMQL promqlParam* promqlQueryPart+
;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.elasticsearch.xpack.core.async.AsyncExecutionId;
import org.elasticsearch.xpack.esql.Column;
import org.elasticsearch.xpack.esql.parser.QueryParams;
import org.elasticsearch.xpack.esql.plan.EsqlStatement;
import org.elasticsearch.xpack.esql.plugin.EsqlQueryStatus;
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;

Expand Down Expand Up @@ -66,6 +67,14 @@ public class EsqlQueryRequest extends org.elasticsearch.xpack.core.esql.action.E
*/
private final Map<String, Map<String, Column>> tables = new TreeMap<>();

/**
* An optional pre-built statement that bypasses ES|QL string parsing.
* This is transient and never serialized over the wire. It's used by internal callers
* (such as the Prometheus REST endpoints) that construct a {@link EsqlStatement} directly
* instead of going through ES|QL string construction and parsing.
*/
private EsqlStatement parsedStatement;

public static EsqlQueryRequest syncEsqlQueryRequest(String query) {
return new EsqlQueryRequest(false, query);
}
Expand All @@ -74,6 +83,17 @@ public static EsqlQueryRequest asyncEsqlQueryRequest(String query) {
return new EsqlQueryRequest(true, query);
}

/**
* Creates a synchronous request with a pre-built statement, bypassing ES|QL string parsing.
* The query string is only used for logging/display since the plan is already built.
*/
public static EsqlQueryRequest syncEsqlQueryRequestWithPlan(EsqlStatement statement) {
String queryText = statement.plan().sourceText();
EsqlQueryRequest request = new EsqlQueryRequest(false, queryText.isEmpty() ? "[pre-built plan]" : queryText);
request.parsedStatement = statement;
return request;
}

private EsqlQueryRequest(boolean async, String query) {
this.async = async;
this.query = query;
Expand Down Expand Up @@ -119,6 +139,13 @@ public String query() {
return query;
}

/**
* Returns the pre-built statement, or {@code null} if the query string should be parsed.
*/
public EsqlStatement parsedStatement() {
return parsedStatement;
}

public boolean async() {
return async;
}
Expand Down

Large diffs are not rendered by default.

Loading
Loading