Skip to content

Conversation

luigidellaquila
Copy link
Contributor

Adding grammar for a SET instruction (not properly a command) to pass settings to a query

SET setting1="value1", setting2="value2"; FROM idx | WHERE foo > 4

import java.util.List;
import java.util.Objects;

public class QuerySettings {
Copy link
Contributor Author

@luigidellaquila luigidellaquila Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not treating this as a command or as part of the AST in general (ie. it's not a LogicalPlan or a Node).
It's on purpose, we don't want it to be part of the query (for now at least), so it doesn't need resolution/serialization/traversal for now.

Ah, I'm not calling it Set. There is only one Set in Java.
Suggestions for a better name are welcome of course

@elasticsearchmachine
Copy link
Collaborator

Hi @luigidellaquila, I've created a changelog YAML for you.

Comment on lines +27 to +29
: {this.isDevVersion()}? setCommand+ singleStatement EOF
| singleStatement EOF
;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will become

statements
    : setCommand* singleStatement EOF
    ;

as soon as it's out of dev mode

@luigidellaquila luigidellaquila changed the title ES|QL: Add SET instruction ES|QL: Add grammar for SET instruction Sep 3, 2025
@luigidellaquila luigidellaquila marked this pull request as ready for review September 3, 2025 10:13
@elasticsearchmachine elasticsearchmachine added the Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) label Sep 3, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-analytical-engine (Team:Analytics)


public class QuerySettings {

private final List<Alias> fields;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it is worth storing it in a name->value map? I suspect it will be accessed by name most of the times

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized we might want to store multiple settings with same name in order to be able to resolve correct one as well as warn about others. Probably list is fine as long as we have an accessor by setting name as well as a flat structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @idegtiarenko, I added a higher level method EsqlQuery.setting(name) that encapsulates the logic for value masking (last one wins).
I also refactored the tests a bit, now they should be more readable.

assertThat(query.settings().size(), is(1));
assertThat(query.settings().get(0).fields().size(), is(1));
assertThat(query.settings().get(0).fields().get(0).name(), is("foo"));
assertThat(query.settings().get(0).fields().get(0).child().fold(FoldContext.small()), is(BytesRefs.toBytesRef("bar")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit too complex with 2 nested lists. Is it possible to collect query settings into a flat structure?
I imagine usage could become much simpler if we have something like assertThat(query.settings().setting("foo").fold(..), is(..))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I simplified this, both with an API method and with some helper methods in the test class, so that I can both check the correctness of the high level logic and the small details in the parsed objects.

@luigidellaquila
Copy link
Contributor Author

luigidellaquila commented Sep 4, 2025

Following up on the off-line discussions, we have two options here:

  • only allow a single field per SET: SET foo=1;
  • allow multiple fields in the same SET: SET foo=1, bar=2, baz=3;

In both cases we'll allow multiple SET statements in the same query, eg. the following will be valid also with the second option.

SET foo=1, bar=2;
SET baz=3;
FROM idx | WHERE x > 3

My understanding is that Kibana will use one field per SET, but as a user I tend to prefer a single SET and a comma separated list of field=value. And it's consistent with EVAL, so I guess it's what users will expect in general.

The PR now implements the second option (multiple fields in the same SET, multiple SET in the same query), but we can easily add limitations if we think it's too much.

@quackaplop WDYT?

Copy link
Contributor

@idegtiarenko idegtiarenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 from my side.
I believe this change could be merged once we have confirmation on multiple key-value pairs per SET

@quackaplop
Copy link

  • only allow a single field per SET

We should only allow a single field per set. We can consider expanding it further, but we don't need this now

: SET setFields SEMICOLON
;

setFields

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure we do not need this one

@luigidellaquila
Copy link
Contributor Author

Thanks @quackaplop, I simplified the grammar and the code in general, now we only allow a single field per SET


import java.util.Objects;

public class QuerySetting {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add toString here?

import java.util.List;

public record EsqlStatement(LogicalPlan plan, List<QuerySetting> settings) {
/**

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add toString here mainly for reporting? Also, how do we feel about this sitting under "plan.logical", given that it is actually not deriving from LogicalPlan?

Copy link
Contributor Author

@luigidellaquila luigidellaquila Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how do we feel about this sitting under "plan.logical", given that it is actually not deriving from LogicalPlan

I don't have a strong opinion on this, I put it there just because it gets parsed together with the logical plan, but "logically" it's a different thing. Let me move it to a separate package

private EsqlStatement parse(String query, QueryParams params) {
var parsed = new EsqlParser().createQuery(query, params, planTelemetry, configuration);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed logical plan:\n{}", parsed.plan());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and maybe using toString here instead?

@luigidellaquila
Copy link
Contributor Author

Thanks for the feedback @idegtiarenko @quackaplop, if there are no further observations I'll go on and merge

@luigidellaquila luigidellaquila merged commit 177a56a into elastic:main Sep 9, 2025
33 checks passed
elasticsearchmachine pushed a commit that referenced this pull request Sep 9, 2025
…SET and INLINESTATS commands) (#134358)

Three PRs have just been merged that cause failures in `test-release`,
and a third PR changes ES|QL railroad diagrams for another recently
released change to `KNN` (also a snapshot-only feature).

* #132729 - `DecayTests` has over 300 failures
* #134029 - `ParsingTests` had six new failures
* #124725 - Failures in `VerifierTests` and `OptimizerVerificationTests`

This PR attempts to fix all in one, because if we do this in separate
PRs they will fail due to the failures cause by the other PRs.
rjernst pushed a commit to rjernst/elasticsearch that referenced this pull request Sep 9, 2025
Kubik42 pushed a commit to Kubik42/elasticsearch that referenced this pull request Sep 9, 2025
Kubik42 pushed a commit to Kubik42/elasticsearch that referenced this pull request Sep 9, 2025
…SET and INLINESTATS commands) (elastic#134358)

Three PRs have just been merged that cause failures in `test-release`,
and a third PR changes ES|QL railroad diagrams for another recently
released change to `KNN` (also a snapshot-only feature).

* elastic#132729 - `DecayTests` has over 300 failures
* elastic#134029 - `ParsingTests` had six new failures
* elastic#124725 - Failures in `VerifierTests` and `OptimizerVerificationTests`

This PR attempts to fix all in one, because if we do this in separate
PRs they will fail due to the failures cause by the other PRs.
vadimkibana added a commit to elastic/kibana that referenced this pull request Oct 6, 2025
## Summary

Closes #233942

Implements parsing for `SET` pseudo-commands, which come before the main
query.

```
SET key1 = 123;
SET key2 = "asdf";
FROM index | LIMIT 123
```

In `query` AST node introduces `header` section, which contain AST nodes
before the main command.

Although each `SET` instruction can now have a single key-value pair, it
is implemented such that it will be able to store a list of those pairs.
The reason is because in Elasticsearch team first implemented it to hold
multiple key-value pairs, then changed to store only one, further they
commented ([see
thread](elastic/elasticsearch#134029)) that they
may add support for a list of key-value pairs to `SET` back.


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
rylnd pushed a commit to rylnd/kibana that referenced this pull request Oct 17, 2025
## Summary

Closes elastic#233942

Implements parsing for `SET` pseudo-commands, which come before the main
query.

```
SET key1 = 123;
SET key2 = "asdf";
FROM index | LIMIT 123
```

In `query` AST node introduces `header` section, which contain AST nodes
before the main command.

Although each `SET` instruction can now have a single key-value pair, it
is implemented such that it will be able to store a list of those pairs.
The reason is because in Elasticsearch team first implemented it to hold
multiple key-value pairs, then changed to store only one, further they
commented ([see
thread](elastic/elasticsearch#134029)) that they
may add support for a list of key-value pairs to `SET` back.


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Analytics/ES|QL AKA ESQL >enhancement Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants