Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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 @@ -258,10 +258,12 @@ static CsvSpecReader.CsvTestCase convertToRemoteIndices(CsvSpecReader.CsvTestCas
String[] localIndices = fromStatement.substring("FROM ".length()).split(",");
final String remoteIndices;
if (canUseRemoteIndicesOnly() && randomBoolean()) {
remoteIndices = Arrays.stream(localIndices).map(index -> "*:" + index.trim()).collect(Collectors.joining(","));
remoteIndices = Arrays.stream(localIndices)
.map(index -> unquoteAndRequoteAsRemote(index.trim(), true))
.collect(Collectors.joining(","));
} else {
remoteIndices = Arrays.stream(localIndices)
.map(index -> "*:" + index.trim() + "," + index.trim())
.map(index -> unquoteAndRequoteAsRemote(index.trim(), false))
.collect(Collectors.joining(","));
}
var newFrom = "FROM " + remoteIndices + " " + commands[0].substring(fromStatement.length());
Expand All @@ -272,9 +274,13 @@ static CsvSpecReader.CsvTestCase convertToRemoteIndices(CsvSpecReader.CsvTestCas
assert parts.length >= 2 : commands[0];
String[] indices = parts[1].split(",");
if (canUseRemoteIndicesOnly() && randomBoolean()) {
parts[1] = Arrays.stream(indices).map(index -> "*:" + index.trim()).collect(Collectors.joining(","));
parts[1] = Arrays.stream(indices)
.map(index -> unquoteAndRequoteAsRemote(index.trim(), true))
.collect(Collectors.joining(","));
} else {
parts[1] = Arrays.stream(indices).map(index -> "*:" + index.trim() + "," + index.trim()).collect(Collectors.joining(","));
parts[1] = Arrays.stream(indices)
.map(index -> unquoteAndRequoteAsRemote(index.trim(), false))
.collect(Collectors.joining(","));
}
String newNewMetrics = String.join(" ", parts);
testCase.query = newNewMetrics + query.substring(first.length());
Expand Down Expand Up @@ -307,6 +313,40 @@ static boolean hasIndexMetadata(String query) {
return false;
}

/**
* Since partial quoting is prohibited, we need to take the index name, unquote it,
* convert it to a remote index, and then requote it. For example, "employees" is unquoted,
* turned into the remote index *:employees, and then requoted to get "*:employees".
* @param index Name of the index.
* @param asRemoteIndexOnly If the return needs to be in the form of "*:idx,idx" or "*:idx".
* @return A remote index pattern that's requoted.
*/
private static String unquoteAndRequoteAsRemote(String index, boolean asRemoteIndexOnly) {
index = index.trim();

int numOfQuotes = 0;
for (; numOfQuotes < index.length(); numOfQuotes++) {
if (index.charAt(numOfQuotes) != '"') {
break;
}
}

String unquoted = unquote(index, numOfQuotes);
if (asRemoteIndexOnly) {
return quote("*:" + unquoted, numOfQuotes);
} else {
return quote("*:" + unquoted + "," + unquoted, numOfQuotes);
}
}

private static String quote(String index, int numOfQuotes) {
return "\"".repeat(numOfQuotes) + index + "\"".repeat(numOfQuotes);
}

private static String unquote(String index, int numOfQuotes) {
return index.substring(numOfQuotes, index.length() - numOfQuotes);
}

@Override
protected boolean enableRoundingDoubleValuesOnAsserting() {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1071,8 +1071,6 @@ public void testDataStreamPatterns() throws Exception {
testCases.put("test_ds_patterns*::data,test_ds_patterns*::failures,-test_ds_patterns_2*::data", 19L);
testCases.put("test_ds_patterns*::data,test_ds_patterns*::failures,-test_ds_patterns_2*::failures", 21L);

testCases.put("\"test_ds_patterns_1,test_ds_patterns_2\"::failures", 8L);

runDataStreamTest(testCases, new String[] { "test_ds_patterns_1", "test_ds_patterns_2", "test_ds_patterns_3" }, (key, value) -> {
try (var results = run("from " + key + " | stats count(@timestamp)")) {
assertEquals(key, 1, getValuesList(results).size());
Expand All @@ -1097,7 +1095,7 @@ public void testDataStreamInvalidPatterns() throws Exception {
// Only one selector separator is allowed per expression
testCases.put("::::data", "mismatched input '::' expecting {QUOTED_STRING, UNQUOTED_SOURCE}");
// Suffix case is not supported because there is no component named with the empty string
testCases.put("index::", "missing {QUOTED_STRING, UNQUOTED_SOURCE} at '|'");
testCases.put("index::", "missing UNQUOTED_SOURCE at '|'");

runDataStreamTest(testCases, new String[] { "test_ds_patterns_1" }, (key, value) -> {
logger.info(key);
Expand Down
11 changes: 7 additions & 4 deletions x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -108,18 +108,21 @@ indexPatternAndMetadataFields:
;

indexPattern
: (clusterString COLON)? indexString
| indexString (CAST_OP selectorString)?
: clusterString COLON unquotedIndexString
| unquotedIndexString CAST_OP selectorString
| indexString
Copy link
Contributor

Choose a reason for hiding this comment

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

Is indexString used anywhere else?

If no, i wonder if that is going to be a bit more readable to have:

indexPattern
    : (clusterString COLON)? unquotedIndexString (CAST_OP selectorString)?
    | quotedIndexString

Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't remote:index::data be supported as well?

Copy link
Contributor

@pawankartik-elastic pawankartik-elastic Jun 17, 2025

Choose a reason for hiding this comment

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

  1. We do not support selector strings for remote indices yet. This functionality is not added to ES.
  2. indexString is definitely used. Consider: FROM "my_index_name". In this case, the index name is represented by indexString and is a QUOTED_STRING. Look at line 128.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@idegtiarenko , your suggestion would be more compact and I considered this at first; but the current approach avoids remote:index::data directly in the grammar - leaving less room for missing a validation, until selectors will be made available for remote indices.

;

clusterString
: UNQUOTED_SOURCE
| QUOTED_STRING
;

selectorString
: UNQUOTED_SOURCE
| QUOTED_STRING
;

unquotedIndexString
: UNQUOTED_SOURCE
;

indexString
Expand Down

Large diffs are not rendered by default.

Loading