SQL: Ignore the _default_ type

It is allowed even in single type indices because it isn't real. We should
ignore it entirely.

Original commit: elastic/x-pack-elasticsearch@efc2cf80c8
This commit is contained in:
Nik Everett 2017-08-24 17:43:22 -04:00
parent 764d802bef
commit 337e9f4d6e
3 changed files with 132 additions and 30 deletions

View File

@ -5,15 +5,20 @@
*/
package org.elasticsearch.xpack.sql.analysis.catalog;
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
@ -43,10 +48,7 @@ public class EsCatalog implements Catalog {
if (idx == null) {
return null;
}
if (false == indexHasOnlyOneType(idx)) {
throw new SqlIllegalArgumentException("index has more than one type");
}
return EsIndex.build(idx);
return EsIndex.build(idx, singleType(idx, false));
}
@Override
@ -69,16 +71,48 @@ public class EsCatalog implements Catalog {
// filter unsupported (indices with more than one type) indices
while (indexMetadata.hasNext()) {
IndexMetaData imd = indexMetadata.next();
if (indexHasOnlyOneType(imd)) {
list.add(EsIndex.build(imd));
MappingMetaData type = singleType(imd, true);
if (type != null) {
list.add(EsIndex.build(imd, type));
}
}
return list;
}
private boolean indexHasOnlyOneType(IndexMetaData index) {
return index.getMappings().size() <= 1;
/**
* Return the single type in the index of {@code null} if there
* are no types in the index.
* @param badIndicesAreNull if true then return null for indices with
* more than one type, if false throw an exception for such indices
*/
@Nullable
private MappingMetaData singleType(IndexMetaData index, boolean badIndicesAreNull) {
/* We actually ignore the _default_ mapping because it is still
* allowed but deprecated. */
MappingMetaData result = null;
List<String> typeNames = null;
for (ObjectObjectCursor<String, MappingMetaData> type : index.getMappings()) {
if ("_default_".equals(type.key)) {
continue;
}
if (result != null) {
if (badIndicesAreNull) {
return null;
}
if (typeNames == null) {
typeNames = new ArrayList<>();
typeNames.add(result.type());
}
typeNames.add(type.key);
}
result = type.value;
}
if (typeNames == null) {
return result;
}
Collections.sort(typeNames);
throw new IllegalArgumentException("[" + index.getIndex().getName() + "] has more than one type " + typeNames);
}
private String[] resolveIndex(String pattern) {

View File

@ -7,16 +7,13 @@ package org.elasticsearch.xpack.sql.analysis.catalog;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
import org.elasticsearch.xpack.sql.type.DataType;
import org.elasticsearch.xpack.sql.type.Types;
import org.elasticsearch.xpack.sql.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -56,24 +53,8 @@ public class EsIndex {
return settings;
}
static List<EsIndex> build(Iterator<IndexMetaData> metadata) {
if (metadata == null || !metadata.hasNext()) {
return emptyList();
}
List<EsIndex> list = new ArrayList<>();
while (metadata.hasNext()) {
build(metadata.next());
}
return list;
}
static EsIndex build(IndexMetaData metadata) {
ImmutableOpenMap<String, MappingMetaData> mappings = metadata.getMappings();
if (mappings.size() > 1) {
throw new SqlIllegalArgumentException("Cannot use index [%s] as it contains multiple types %s", metadata.getIndex().getName(), Arrays.toString(mappings.keys().toArray()));
}
MappingMetaData mm = mappings.isEmpty() ? null : mappings.valuesIt().next();
Map<String, DataType> mapping = mm != null ? Types.fromEs(mm.sourceAsMap()) : emptyMap();
static EsIndex build(IndexMetaData metadata, @Nullable MappingMetaData type) {
Map<String, DataType> mapping = type != null ? Types.fromEs(type.sourceAsMap()) : emptyMap();
List<String> aliases = Arrays.asList(metadata.getAliases().keys().toArray(String.class));
return new EsIndex(metadata.getIndex().getName(), mapping, aliases, metadata.getSettings());

View File

@ -0,0 +1,87 @@
/*
* 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.analysis.catalog;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.List;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.hamcrest.Matchers.hasSize;
public class EsCatalogTests extends ESTestCase {
public void testEmpty() {
EsCatalog catalog = catalog(ClusterState.builder(ClusterName.DEFAULT).build());
assertEquals(emptyList(), catalog.listIndices("*"));
assertNull(catalog.getIndex("test"));
}
public void testIndexExists() throws IOException {
EsCatalog catalog = catalog(ClusterState.builder(ClusterName.DEFAULT)
.metaData(MetaData.builder()
.put(index("test")
.putMapping("test", "{}"))
.build())
.build());
List<EsIndex> indices = catalog.listIndices("*");
assertThat(indices, hasSize(1));
assertEquals("test", indices.get(0).name());
assertEquals(emptyMap(), catalog.getIndex("test").mapping());
}
public void testIndexWithDefaultType() throws IOException {
EsCatalog catalog = catalog(ClusterState.builder(ClusterName.DEFAULT)
.metaData(MetaData.builder()
.put(index("test")
.putMapping("test", "{}")
.putMapping("_default_", "{}"))
.build())
.build());
List<EsIndex> indices = catalog.listIndices("*");
assertThat(indices, hasSize(1));
assertEquals("test", indices.get(0).name());
assertEquals(emptyMap(), catalog.getIndex("test").mapping());
}
public void testIndexWithTwoTypes() throws IOException {
EsCatalog catalog = catalog(ClusterState.builder(ClusterName.DEFAULT)
.metaData(MetaData.builder()
.put(index("test")
.putMapping("first_type", "{}")
.putMapping("second_type", "{}"))
.build())
.build());
assertEquals(emptyList(), catalog.listIndices("*"));
Exception e = expectThrows(IllegalArgumentException.class, () -> catalog.getIndex("test"));
assertEquals(e.getMessage(), "[test] has more than one type [first_type, second_type]");
}
private EsCatalog catalog(ClusterState state) {
EsCatalog catalog = new EsCatalog(() -> state);
catalog.setIndexNameExpressionResolver(new IndexNameExpressionResolver(Settings.EMPTY));
return catalog;
}
private IndexMetaData.Builder index(String name) throws IOException {
return IndexMetaData.builder("test")
.settings(Settings.builder()
.put("index.version.created", Version.CURRENT)
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 1));
}
}