Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,13 @@ public void testInsufficientPrivilege() {
() -> runESQLCommand("metadata1_read2", "FROM index-user1 | STATS sum=sum(value)")
);
logger.info("error", error);
assertThat(error.getMessage(), containsString("Unknown index [index-user1]"));
assertThat(error.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_BAD_REQUEST));
assertThat(
error.getMessage(),
containsString(
"unauthorized for user [test-admin] run as [metadata1_read2] with effective roles [metadata1_read2] on indices [index-user1]"
)
);
assertThat(error.getResponse().getStatusLine().getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN));
}

public void testIndexPatternErrorMessageComparison_ESQL_SearchDSL() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public abstract class EsqlRestValidationTestCase extends ESRestTestCase {
aliasName + "*,inexistent*",
"inexistent*," + aliasName };
private static final String[] existentAliasWithoutWildcard = new String[] { aliasName + ",inexistent", "inexistent," + aliasName };
private static final String[] inexistentIndexNameWithWildcard = new String[] { "inexistent*", "inexistent1*,inexistent2*" };
private static final String[] inexistentIndexNameWithoutWildcard = new String[] { "inexistent", "inexistent1,inexistent2" };
private static final String createAlias = "{\"actions\":[{\"add\":{\"index\":\"" + indexName + "\",\"alias\":\"" + aliasName + "\"}}]}";
private static final String removeAlias = "{\"actions\":[{\"remove\":{\"index\":\""
Expand Down Expand Up @@ -73,21 +72,20 @@ public void wipeTestData() throws IOException {
}
}

private String getInexistentIndexErrorMessage() {
return "\"reason\" : \"Found 1 problem\\nline 1:1: Unknown index ";
}

public void testInexistentIndexNameWithWildcard() throws IOException {
assertErrorMessages(inexistentIndexNameWithWildcard, getInexistentIndexErrorMessage(), 400);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now wildcards are allowed to expand to empty result set


public void testInexistentIndexNameWithoutWildcard() throws IOException {
assertErrorMessages(inexistentIndexNameWithoutWildcard, getInexistentIndexErrorMessage(), 400);
for (String indexName : inexistentIndexNameWithoutWildcard) {
// TODO ES-13095 all from indexName should be reported as missing.
assertErrorMessage(
indexName,
"\"reason\" : \"Found 1 problem\\nline 1:1: Unknown index [" + clusterSpecificIndexName("inexistent"),
400
);
}
}

public void testExistentIndexWithoutWildcard() throws IOException {
for (String indexName : existentIndexWithoutWildcard) {
assertErrorMessage(indexName, "\"reason\" : \"no such index [inexistent]\"", 404);
assertErrorMessage(indexName, "\"reason\" : \"Found 1 problem\\nline 1:1: Unknown index [inexistent]\"", 400);
}
}

Expand All @@ -99,19 +97,13 @@ public void testAlias() throws IOException {
createAlias();

for (String indexName : existentAliasWithoutWildcard) {
assertErrorMessage(indexName, "\"reason\" : \"no such index [inexistent]\"", 404);
assertErrorMessage(indexName, "\"reason\" : \"Found 1 problem\\nline 1:1: Unknown index [inexistent]\"", 400);
}
assertValidRequestOnIndices(existentAliasWithWildcard);

deleteAlias();
}

private void assertErrorMessages(String[] indices, String errorMessage, int statusCode) throws IOException {
for (String indexName : indices) {
assertErrorMessage(indexName, errorMessage + "[" + clusterSpecificIndexName(indexName) + "]", statusCode);
}
}

protected String clusterSpecificIndexName(String indexName) {
return indexName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,11 @@ public void testTimestampFilterFromQuery() throws IOException {
allOf(instanceOf(List.class), hasSize(docsTest1))
);

// filter excludes both indices (no rows); the first analysis step fails because there are no columns, a second attempt succeeds
// after eliminating the index filter. All columns are returned.
// filter excludes both indices (no rows); Empty result set is derived.
builder = timestampFilter("gte", "2025-01-01").query(from("test*"));
assertQueryResult(
runEsql(builder),
matchesList().item(matchesMap().entry("name", "@timestamp").entry("type", "date"))
.item(matchesMap().entry("name", "id1").entry("type", "integer"))
.item(matchesMap().entry("name", "id2").entry("type", "integer"))
.item(matchesMap().entry("name", "value").entry("type", "long")),
matchesList().item(matchesMap().entry("name", "<no-fields>").entry("type", "null")),
allOf(instanceOf(List.class), hasSize(0))
);
}
Expand Down Expand Up @@ -206,15 +202,10 @@ public void testIndicesDontExist() throws IOException {
assertThat(e.getMessage(), containsString("verification_exception"));
assertThat(e.getMessage(), anyOf(containsString("Unknown index [foo]"), containsString("Unknown index [remote_cluster:foo]")));

e = expectThrows(ResponseException.class, () -> runEsql(timestampFilter("gte", "2020-01-01").query(from("foo*"))));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The same, foo* can now expand to empty result set

e = expectThrows(ResponseException.class, () -> runEsql(timestampFilter("gte", "2020-01-01").query("FROM foo,test1")));
assertEquals(400, e.getResponse().getStatusLine().getStatusCode());
assertThat(e.getMessage(), containsString("verification_exception"));
assertThat(e.getMessage(), anyOf(containsString("Unknown index [foo*]"), containsString("Unknown index [remote_cluster:foo*]")));

e = expectThrows(ResponseException.class, () -> runEsql(timestampFilter("gte", "2020-01-01").query("FROM foo, test1")));
assertEquals(404, e.getResponse().getStatusLine().getStatusCode());
assertThat(e.getMessage(), containsString("index_not_found_exception"));
assertThat(e.getMessage(), containsString("no such index [foo]"));
assertThat(e.getMessage(), containsString("Unknown index [foo,test1]"));// TODO why test1 is reported?

// Don't test remote patterns here, we'll test them in the multi-cluster tests
if (EsqlCapabilities.Cap.JOIN_LOOKUP_V12.isEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.elasticsearch.compute.operator.DriverProfile;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.core.Tuple;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.test.FailingFieldPlugin;
Expand Down Expand Up @@ -133,15 +132,10 @@ public void testSearchesAgainstNonMatchingIndicesWithLocalOnly() throws Exceptio

{
String q = "FROM nomatch," + localIndex;
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> runQuery(q, false));
assertThat(e.getDetailedMessage(), containsString("no such index [nomatch]"));

// MP TODO: am I able to fix this from the field-caps call? Yes, if we detect concrete vs. wildcard expressions in user query
// TODO bug - this does not throw; uncomment this test once this is fixed:
// AwaitsFix https://github.com/elastic/elasticsearch/issues/114495
// String limit0 = q + " | LIMIT 0";
// VerificationException ve = expectThrows(VerificationException.class, () -> runQuery(limit0, false));
// assertThat(ve.getDetailedMessage(), containsString("No matching indices for [nomatch]"));
expectThrows(VerificationException.class, containsString("Unknown index [nomatch]"), () -> runQuery(q, false));

String limit0 = q + " | LIMIT 0";
expectThrows(VerificationException.class, containsString("Unknown index [nomatch]"), () -> runQuery(limit0, false));
}

{
Expand All @@ -168,11 +162,6 @@ public void testSearchesAgainstNonMatchingIndicesWithLocalOnly() throws Exceptio
String expectedError = "Unknown index [nomatch]";
expectVerificationExceptionForQuery(q, expectedError, false);
}
{
String q = "FROM nomatch*";
String expectedError = "Unknown index [nomatch*]";
expectVerificationExceptionForQuery(q, expectedError, false);
}
}

public void testSearchesAgainstIndicesWithNoMappingsSkipUnavailableTrue() throws Exception {
Expand Down Expand Up @@ -234,10 +223,6 @@ public void testSearchesAgainstIndicesWithNoMappingsSkipUnavailableTrue() throws
}

public void testSearchesAgainstNonMatchingIndices() throws Exception {
testSearchesAgainstNonMatchingIndices(true);
}

protected void testSearchesAgainstNonMatchingIndices(boolean exceptionWithSkipUnavailableFalse) throws Exception {
int numClusters = 3;
Map<String, Object> testClusterInfo = setupClusters(numClusters);
int localNumShards = (Integer) testClusterInfo.get("local.num_shards");
Expand All @@ -264,11 +249,9 @@ protected void testSearchesAgainstNonMatchingIndices(boolean exceptionWithSkipUn
{
String q = "FROM logs*,cluster-a:nomatch";
String expectedError = "Unknown index [cluster-a:nomatch]";
if (exceptionWithSkipUnavailableFalse) {
setSkipUnavailable(REMOTE_CLUSTER_1, false);
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);
setSkipUnavailable(REMOTE_CLUSTER_1, true);
}
setSkipUnavailable(REMOTE_CLUSTER_1, false);
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);
setSkipUnavailable(REMOTE_CLUSTER_1, true);
try (EsqlQueryResponse resp = runQuery(q, requestIncludeMeta)) {
assertThat(getValuesList(resp).size(), greaterThanOrEqualTo(1));
EsqlExecutionInfo executionInfo = resp.getExecutionInfo();
Expand Down Expand Up @@ -370,14 +353,7 @@ protected void testSearchesAgainstNonMatchingIndices(boolean exceptionWithSkipUn
{
String q = "FROM cluster-a:nomatch";
String expectedError = "Unknown index [cluster-a:nomatch]";
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);
}

// an error is thrown if there are no matching indices at all - single remote cluster with wildcard index expression
{
String q = "FROM cluster-a:nomatch*";
String expectedError = "Unknown index [cluster-a:nomatch*]";
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);// TODO this must contain the remote cluster alias
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here we are getting RemoteTransportException with cause IndexNotFoundException with index nonmatch. It is correct, but it does not give me enough information to point out to cluster-a in case there are multiple remotes involved. This is likely needs to be added on FieldCaps side

}

// an error is thrown if there is a concrete index that does not match
Expand Down Expand Up @@ -412,11 +388,9 @@ protected void testSearchesAgainstNonMatchingIndices(boolean exceptionWithSkipUn
String remote2IndexName = randomFrom(remote2Index, IDX_ALIAS, FILTERED_IDX_ALIAS);
String q = Strings.format("FROM %s*,cluster-a:nomatch,%s:%s*", localIndexName, REMOTE_CLUSTER_2, remote2IndexName);
String expectedError = "Unknown index [cluster-a:nomatch]";
if (exceptionWithSkipUnavailableFalse) {
setSkipUnavailable(REMOTE_CLUSTER_1, false);
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);
setSkipUnavailable(REMOTE_CLUSTER_1, true);
}
setSkipUnavailable(REMOTE_CLUSTER_1, false);
expectVerificationExceptionForQuery(q, expectedError, requestIncludeMeta);
setSkipUnavailable(REMOTE_CLUSTER_1, true);
try (EsqlQueryResponse resp = runQuery(q, requestIncludeMeta)) {
assertThat(getValuesList(resp).size(), greaterThanOrEqualTo(1));
EsqlExecutionInfo executionInfo = resp.getExecutionInfo();
Expand Down Expand Up @@ -452,12 +426,8 @@ record ExpectedCluster(String clusterAlias, String indexExpression, EsqlExecutio
* extra processing step to ensure that ESQL coordinator-only operations throw the same VerificationError.
*/
protected void expectVerificationExceptionForQuery(String query, String error, Boolean requestIncludeMeta) {
VerificationException e = expectThrows(VerificationException.class, () -> runQuery(query, requestIncludeMeta));
assertThat(e.getDetailedMessage(), containsString(error));

String limit0 = query + " | LIMIT 0";
e = expectThrows(VerificationException.class, () -> runQuery(limit0, requestIncludeMeta));
assertThat(e.getDetailedMessage(), containsString(error));
expectThrows(VerificationException.class, containsString(error), () -> runQuery(query, requestIncludeMeta));
expectThrows(VerificationException.class, containsString(error), () -> runQuery(query + " | LIMIT 0", requestIncludeMeta));
}

public void assertExpectedClustersForMissingIndicesTests(EsqlExecutionInfo executionInfo, List<ExpectedCluster> expected) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.datastreams.DataStreamsPlugin;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.MatchNoneQueryBuilder;
import org.elasticsearch.plugins.Plugin;
Expand Down Expand Up @@ -112,23 +111,20 @@ public void testResolvesExclusionPattern() {
assertOk(response);
assertResultConcreteIndices(response, "index-1");// excludes pattern from pattern
}
expectThrows(
VerificationException.class,
containsString("Unknown index [index-*,-*]"),
() -> run(syncEsqlQueryRequest().query("FROM index-*,-* METADATA _index")) // exclude all resolves to empty
);
try (var response = run(syncEsqlQueryRequest().query("FROM index-*,-* METADATA _index"))) {
assertOk(response);
assertResultConcreteIndices(response);
}
}

public void testDoesNotResolveEmptyPattern() {
assertAcked(client().admin().indices().prepareCreate("data"));
indexRandom(true, "data", 1);

expectThrows(
VerificationException.class,
containsString("Unknown index [index-*]"),
() -> run(syncEsqlQueryRequest().query("FROM index-* METADATA _index"))
);

try (var response = run(syncEsqlQueryRequest().query("FROM index-* METADATA _index"))) {
assertOk(response);
assertResultConcreteIndices(response);
}
try (var response = run(syncEsqlQueryRequest().query("FROM data,index-* METADATA _index"))) {
assertOk(response);
assertResultConcreteIndices(response, "data");
Expand Down Expand Up @@ -224,21 +220,20 @@ public void testUnavailableIndex() {

public void testPartialResolution() {
assertAcked(client().admin().indices().prepareCreate("index-1"));
assertAcked(client().admin().indices().prepareCreate("index-2"));
indexRandom(true, "index-2", 10);

try (var response = run(syncEsqlQueryRequest().query("FROM index-1,nonexisting-1"))) {
assertOk(response); // okay when present index is empty
if (randomBoolean()) {
indexRandom(true, "index-1", 1);
}

expectThrows(
IndexNotFoundException.class,
equalTo("no such index [nonexisting-1]"), // fails when present index is non-empty
() -> run(syncEsqlQueryRequest().query("FROM index-2,nonexisting-1"))
VerificationException.class,
containsString("Unknown index [nonexisting-1]"),
() -> run(syncEsqlQueryRequest().query("FROM index-1,nonexisting-1"))
);
expectThrows(
IndexNotFoundException.class,
equalTo("no such index [nonexisting-1]"), // only the first missing index is reported
() -> run(syncEsqlQueryRequest().query("FROM index-2,nonexisting-1,nonexisting-2"))
VerificationException.class,
// TODO ES-13095 both [nonexisting-1,nonexisting-2] missing should be reported as missing.
containsString("Unknown index [nonexisting-1]"), // only the first missing index is reported
() -> run(syncEsqlQueryRequest().query("FROM index-1,nonexisting-1,nonexisting-2"))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ public static IndexResolution invalid(String invalid) {
return new IndexResolution(null, invalid, Set.of(), Map.of());
}

public static IndexResolution empty(String indexPattern) {
return valid(new EsIndex(indexPattern, Map.of(), Map.of()));
}

public static IndexResolution notFound(String name) {
Objects.requireNonNull(name, "name must not be null");
return invalid("Unknown index [" + name + "]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,9 +696,7 @@ private void preAnalyzeMainIndices(
String indexExpressionToResolve = EsqlCCSUtils.createIndexExpressionFromAvailableClusters(executionInfo);
if (indexExpressionToResolve.isEmpty()) {
// if this was a pure remote CCS request (no local indices) and all remotes are offline, return an empty IndexResolution
listener.onResponse(
result.withIndices(IndexResolution.valid(new EsIndex(preAnalysis.indexPattern().indexPattern(), Map.of(), Map.of())))
);
listener.onResponse(result.withIndices(IndexResolution.empty(preAnalysis.indexPattern().indexPattern())));
} else {
indexResolver.resolveAsMergedMapping(
indexExpressionToResolve,
Expand Down
Loading