SQL: Support multi-index format as table identifier (#33278)
Extend tableIdentifier to support multi-index format; not just * but also enumeration and exclusion Fix #33162
This commit is contained in:
parent
c6cfa08a61
commit
73eb4cbbbe
|
@ -19,6 +19,7 @@ import org.elasticsearch.action.support.IndicesOptions;
|
|||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.xpack.sql.type.EsField;
|
||||
|
@ -300,7 +301,7 @@ public class IndexResolver {
|
|||
private static GetIndexRequest createGetIndexRequest(String index) {
|
||||
return new GetIndexRequest()
|
||||
.local(true)
|
||||
.indices(index)
|
||||
.indices(Strings.commaDelimitedListToStringArray(index))
|
||||
.features(Feature.MAPPINGS)
|
||||
//lenient because we throw our own errors looking at the response e.g. if something was not resolved
|
||||
//also because this way security doesn't throw authorization exceptions but rather honours ignore_unavailable
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.action.search.SearchResponse;
|
|||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.CollectionUtils;
|
||||
|
@ -92,7 +93,7 @@ public class Querier {
|
|||
log.trace("About to execute query {} on {}", StringUtils.toString(sourceBuilder), index);
|
||||
}
|
||||
|
||||
SearchRequest search = prepareRequest(client, sourceBuilder, timeout, index);
|
||||
SearchRequest search = prepareRequest(client, sourceBuilder, timeout, Strings.commaDelimitedListToStringArray(index));
|
||||
|
||||
ActionListener<SearchResponse> l;
|
||||
if (query.isAggsOnly()) {
|
||||
|
|
|
@ -21,23 +21,9 @@ abstract class IdentifierBuilder extends AbstractBuilder {
|
|||
ParseTree tree = ctx.name != null ? ctx.name : ctx.TABLE_IDENTIFIER();
|
||||
String index = tree.getText();
|
||||
|
||||
validateIndex(index, source);
|
||||
return new TableIdentifier(source, visitIdentifier(ctx.catalog), index);
|
||||
}
|
||||
|
||||
// see https://github.com/elastic/elasticsearch/issues/6736
|
||||
static void validateIndex(String index, Location source) {
|
||||
for (int i = 0; i < index.length(); i++) {
|
||||
char c = index.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
throw new ParsingException(source, "Invalid index name (needs to be lowercase) {}", index);
|
||||
}
|
||||
if (c == '\\' || c == '/' || c == '<' || c == '>' || c == '|' || c == ',' || c == ' ') {
|
||||
throw new ParsingException(source, "Invalid index name (illegal character {}) {}", c, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String visitIdentifier(IdentifierContext ctx) {
|
||||
return ctx == null ? null : ctx.getText();
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.parser;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class IdentifierBuilderTests extends ESTestCase {
|
||||
|
||||
private static Location L = new Location(1, 10);
|
||||
|
||||
public void testTypicalIndex() throws Exception {
|
||||
IdentifierBuilder.validateIndex("some-index", L);
|
||||
}
|
||||
|
||||
public void testInternalIndex() throws Exception {
|
||||
IdentifierBuilder.validateIndex(".some-internal-index-2020-02-02", L);
|
||||
}
|
||||
|
||||
public void testIndexPattern() throws Exception {
|
||||
IdentifierBuilder.validateIndex(".some-*", L);
|
||||
}
|
||||
|
||||
public void testInvalidIndex() throws Exception {
|
||||
ParsingException pe = expectThrows(ParsingException.class, () -> IdentifierBuilder.validateIndex("some,index", L));
|
||||
assertThat(pe.getMessage(), is("line 1:12: Invalid index name (illegal character ,) some,index"));
|
||||
}
|
||||
|
||||
public void testUpperCasedIndex() throws Exception {
|
||||
ParsingException pe = expectThrows(ParsingException.class, () -> IdentifierBuilder.validateIndex("thisIsAnIndex", L));
|
||||
assertThat(pe.getMessage(), is("line 1:12: Invalid index name (needs to be lowercase) thisIsAnIndex"));
|
||||
}
|
||||
}
|
|
@ -162,3 +162,27 @@ last_name | VARCHAR
|
|||
last_name.keyword | VARCHAR
|
||||
salary | INTEGER
|
||||
;
|
||||
|
||||
|
||||
describeIncludeExclude
|
||||
DESCRIBE "test_emp*,-test_alias*";
|
||||
|
||||
column:s | type:s
|
||||
birth_date | TIMESTAMP
|
||||
dep | STRUCT
|
||||
dep.dep_id | VARCHAR
|
||||
dep.dep_name | VARCHAR
|
||||
dep.dep_name.keyword | VARCHAR
|
||||
dep.from_date | TIMESTAMP
|
||||
dep.to_date | TIMESTAMP
|
||||
emp_no | INTEGER
|
||||
first_name | VARCHAR
|
||||
first_name.keyword | VARCHAR
|
||||
gender | VARCHAR
|
||||
hire_date | TIMESTAMP
|
||||
languages | TINYINT
|
||||
last_name | VARCHAR
|
||||
last_name.keyword | VARCHAR
|
||||
salary | INTEGER
|
||||
;
|
||||
|
||||
|
|
Loading…
Reference in New Issue