Trim down the Catalog implementations to a single one

Catalog is now a final class rather than an interface with different implementations.

relates elastic/x-pack-elasticsearch#3179

Original commit: elastic/x-pack-elasticsearch@4cc927e113
This commit is contained in:
javanna 2017-12-04 14:57:35 +01:00 committed by Luca Cavanna
parent 8c61cabe87
commit 90aee54251
9 changed files with 41 additions and 78 deletions

View File

@ -128,12 +128,12 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
} }
@Override @Override
public void testSelectInvalidSql() throws Exception { public void testSelectInvalidSql() {
expectBadRequest(() -> runSql("SELECT * FRO"), containsString("1:8: Cannot determine columns for *")); expectBadRequest(() -> runSql("SELECT * FRO"), containsString("1:8: Cannot determine columns for *"));
} }
@Override @Override
public void testSelectFromMissingIndex() throws IOException { public void testSelectFromMissingIndex() {
expectBadRequest(() -> runSql("SELECT * FROM missing"), containsString("1:15: Unknown index [missing]")); expectBadRequest(() -> runSql("SELECT * FROM missing"), containsString("1:15: Unknown index [missing]"));
} }
@ -161,7 +161,7 @@ public abstract class RestSqlTestCase extends ESRestTestCase implements ErrorsTe
private void expectBadRequest(ThrowingRunnable code, Matcher<String> errorMessageMatcher) { private void expectBadRequest(ThrowingRunnable code, Matcher<String> errorMessageMatcher) {
ResponseException e = expectThrows(ResponseException.class, code); ResponseException e = expectThrows(ResponseException.class, code);
assertEquals(400, e.getResponse().getStatusLine().getStatusCode()); assertEquals(e.getMessage(), 400, e.getResponse().getStatusLine().getStatusCode());
assertThat(e.getMessage(), errorMessageMatcher); assertThat(e.getMessage(), errorMessageMatcher);
} }

View File

@ -253,17 +253,13 @@ public class Analyzer extends RuleExecutor<LogicalPlan> {
@Override @Override
protected LogicalPlan rule(UnresolvedRelation plan) { protected LogicalPlan rule(UnresolvedRelation plan) {
TableIdentifier table = plan.table(); TableIdentifier table = plan.table();
EsIndex found = null;
GetIndexResult index = SqlSession.currentContext().catalog.getIndex(table.index()); GetIndexResult index = SqlSession.currentContext().catalog.getIndex(table.index());
if (index.isValid()) { if (index.isValid() == false) {
found = index.get();
} else {
return plan.unresolvedMessage().equals(index.toString()) ? plan : new UnresolvedRelation(plan.location(), plan.table(), return plan.unresolvedMessage().equals(index.toString()) ? plan : new UnresolvedRelation(plan.location(), plan.table(),
plan.alias(), index.toString()); plan.alias(), index.toString());
} }
LogicalPlan catalogTable = new EsRelation(plan.location(), found); LogicalPlan catalogTable = new EsRelation(plan.location(), index.get());
SubQueryAlias sa = new SubQueryAlias(plan.location(), catalogTable, table.index()); SubQueryAlias sa = new SubQueryAlias(plan.location(), catalogTable, table.index());
if (plan.alias() != null) { if (plan.alias() != null) {

View File

@ -5,26 +5,39 @@
*/ */
package org.elasticsearch.xpack.sql.analysis.catalog; package org.elasticsearch.xpack.sql.analysis.catalog;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
/** /**
* Converts from Elasticsearch's internal metadata ({@link ClusterState}) * Index representation that is compatible with SQL ({@link EsIndex}).
* into a representation that is compatible with SQL (@{link {@link EsIndex}).
*/ */
public interface Catalog { public final class Catalog {
Catalog EMPTY = GetIndexResult::notFound; public static final Catalog EMPTY = new Catalog(GetIndexResult::notFound);
private final Function<String, GetIndexResult> resultFunction;
//TODO given that this always holds a single index, we cana probably get rid of the whole idea of Catalog
public Catalog(GetIndexResult result) {
assert result != null;
this.resultFunction = index -> result.matches(index) ? result : GetIndexResult.notFound(index);
}
private Catalog(Function<String, GetIndexResult> resultFunction) {
assert resultFunction != null;
this.resultFunction = resultFunction;
}
/** /**
* Lookup the information for a table. * Lookup the information for a table.
*/ */
@Nullable public GetIndexResult getIndex(String index) {
GetIndexResult getIndex(String index); return resultFunction.apply(index);
}
class GetIndexResult { public static final class GetIndexResult {
public static GetIndexResult valid(EsIndex index) { public static GetIndexResult valid(EsIndex index) {
Objects.requireNonNull(index, "index must not be null if it was found"); Objects.requireNonNull(index, "index must not be null if it was found");
return new GetIndexResult(index, null); return new GetIndexResult(index, null);
@ -47,6 +60,10 @@ public interface Catalog {
this.invalid = invalid; this.invalid = invalid;
} }
private boolean matches(String index) {
return isValid() && this.index.name().equals(index);
}
/** /**
* Get the {@linkplain EsIndex} built by the {@linkplain Catalog}. * Get the {@linkplain EsIndex} built by the {@linkplain Catalog}.
* @throws MappingException if the index is invalid for * @throws MappingException if the index is invalid for

View File

@ -20,6 +20,8 @@ public class EsIndex {
private final Map<String, DataType> mapping; private final Map<String, DataType> mapping;
public EsIndex(String name, Map<String, DataType> mapping) { public EsIndex(String name, Map<String, DataType> mapping) {
assert name != null;
assert mapping != null;
this.name = name; this.name = name;
this.mapping = mapping; this.mapping = mapping;
} }

View File

@ -38,10 +38,10 @@ public class IndexResolver {
public void asCatalog(final String index, ActionListener<Catalog> listener) { public void asCatalog(final String index, ActionListener<Catalog> listener) {
GetIndexRequest getIndexRequest = createGetIndexRequest(index); GetIndexRequest getIndexRequest = createGetIndexRequest(index);
client.admin().indices().getIndex(getIndexRequest, ActionListener.wrap(getIndexResponse -> { client.admin().indices().getIndex(getIndexRequest, ActionListener.wrap(getIndexResponse -> {
Map<String, GetIndexResult> results = new HashMap<>(); GetIndexResult result;
if (getIndexResponse.getMappings().size() > 1) { if (getIndexResponse.getMappings().size() > 1) {
results.put(index, GetIndexResult.invalid( result = GetIndexResult.invalid(
"[" + index + "] is an alias pointing to more than one index which is currently incompatible with sql")); "[" + index + "] is an alias pointing to more than one index which is currently incompatible with sql");
} else if (getIndexResponse.getMappings().size() == 1){ } else if (getIndexResponse.getMappings().size() == 1){
ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexMappings = ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexMappings =
getIndexResponse.getMappings().iterator().next(); getIndexResponse.getMappings().iterator().next();
@ -53,9 +53,11 @@ public class IndexResolver {
* make sure that the search is executed against the same alias name from the original command, rather than * make sure that the search is executed against the same alias name from the original command, rather than
* the resolved concrete index that we get back from the get index API * the resolved concrete index that we get back from the get index API
*/ */
results.put(index, buildGetIndexResult(concreteIndex, index, indexMappings.value)); result = buildGetIndexResult(concreteIndex, index, indexMappings.value);
} else {
result = GetIndexResult.notFound(index);
} }
listener.onResponse(new PreloadedCatalog(results)); listener.onResponse(new Catalog(result));
}, listener::onFailure)); }, listener::onFailure));
} }

View File

@ -1,23 +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.analysis.catalog;
import java.util.Map;
public class PreloadedCatalog implements Catalog {
private final Map<String, GetIndexResult> map;
public PreloadedCatalog(Map<String, GetIndexResult> map) {
this.map = map;
}
@Override
public GetIndexResult getIndex(String index) {
return map.getOrDefault(index, GetIndexResult.notFound(index));
}
}

View File

@ -176,6 +176,7 @@ public class SqlSession {
listener.onFailure(new SqlIllegalArgumentException("Queries with multiple indices are not supported")); listener.onFailure(new SqlIllegalArgumentException("Queries with multiple indices are not supported"));
return; return;
} }
//TODO why do we have a list if we only support one single element? Seems like it's the wrong data structure?
if (preAnalysis.indices.size() == 1) { if (preAnalysis.indices.size() == 1) {
indexResolver.asCatalog(preAnalysis.indices.get(0), indexResolver.asCatalog(preAnalysis.indices.get(0),
wrap(c -> listener.onResponse(action.apply(c)), listener::onFailure)); wrap(c -> listener.onResponse(action.apply(c)), listener::onFailure));

View File

@ -10,7 +10,6 @@ import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.xpack.sql.analysis.AnalysisException; import org.elasticsearch.xpack.sql.analysis.AnalysisException;
import org.elasticsearch.xpack.sql.analysis.catalog.Catalog; import org.elasticsearch.xpack.sql.analysis.catalog.Catalog;
import org.elasticsearch.xpack.sql.analysis.catalog.EsIndex; import org.elasticsearch.xpack.sql.analysis.catalog.EsIndex;
import org.elasticsearch.xpack.sql.analysis.catalog.InMemoryCatalog;
import org.elasticsearch.xpack.sql.expression.function.DefaultFunctionRegistry; import org.elasticsearch.xpack.sql.expression.function.DefaultFunctionRegistry;
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry; import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
import org.elasticsearch.xpack.sql.parser.SqlParser; import org.elasticsearch.xpack.sql.parser.SqlParser;
@ -23,8 +22,6 @@ import org.junit.Before;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import static java.util.Collections.singletonList;
@TestLogging("org.elasticsearch.xpack.sql:TRACE") @TestLogging("org.elasticsearch.xpack.sql:TRACE")
public class VerifierErrorMessagesTests extends ESTestCase { public class VerifierErrorMessagesTests extends ESTestCase {
@ -43,7 +40,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
mapping.put("text", DataTypes.TEXT); mapping.put("text", DataTypes.TEXT);
mapping.put("keyword", DataTypes.KEYWORD); mapping.put("keyword", DataTypes.KEYWORD);
EsIndex test = new EsIndex("test", mapping); EsIndex test = new EsIndex("test", mapping);
catalog = new InMemoryCatalog(singletonList(test)); catalog = new Catalog(Catalog.GetIndexResult.valid(test));
analyzer = new Analyzer(functionRegistry); analyzer = new Analyzer(functionRegistry);
} }

View File

@ -1,29 +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.analysis.catalog;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static java.util.stream.Collectors.toMap;
/**
* In memory implementation of catalog designed for testing.
*/
public class InMemoryCatalog implements Catalog {
private final Map<String, EsIndex> indices;
public InMemoryCatalog(List<EsIndex> indices) {
this.indices = indices.stream().collect(toMap(EsIndex::name, Function.identity()));
}
@Override
public GetIndexResult getIndex(String index) {
EsIndex result = indices.get(index);
return result == null ? GetIndexResult.notFound(index) : GetIndexResult.valid(result);
}
}