/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate licenses * this file to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.analyze.relations; import com.google.common.collect.ImmutableMap; import io.crate.analyze.symbol.Field; import io.crate.exceptions.AmbiguousColumnException; import io.crate.exceptions.ColumnUnknownException; import io.crate.exceptions.RelationUnknownException; import io.crate.metadata.table.Operation; import io.crate.sql.tree.QualifiedName; import io.crate.test.integration.CrateUnitTest; import io.crate.testing.DummyRelation; import org.junit.Test; import java.util.Arrays; import java.util.Map; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.Is.is; public class FieldProviderTest extends CrateUnitTest { private AnalyzedRelation dummyRelation = new DummyRelation("name"); private Map<QualifiedName, AnalyzedRelation> dummySources = ImmutableMap.of( newQN("dummy.t"), dummyRelation); private static QualifiedName newQN(String dottedName) { return new QualifiedName(Arrays.asList(dottedName.split("\\."))); } @Test public void testInvalidSources() throws Exception { expectedException.expect(UnsupportedOperationException.class); AnalyzedRelation relation = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider( ImmutableMap.of(newQN("too.many.parts"), relation)); resolver.resolveField(newQN("name"), Operation.READ); } @Test public void testUnknownSchema() throws Exception { expectedException.expect(RelationUnknownException.class); expectedException.expectMessage("Cannot resolve relation 'invalid.table'"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(dummySources); resolver.resolveField(newQN("invalid.table.name"), Operation.READ); } @Test public void testUnknownTable() throws Exception { expectedException.expect(RelationUnknownException.class); expectedException.expectMessage("Cannot resolve relation 'dummy.invalid'"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(dummySources); resolver.resolveField(newQN("dummy.invalid.name"), Operation.READ); } @Test public void testSysColumnWithoutSourceRelation() throws Exception { expectedException.expect(RelationUnknownException.class); expectedException.expectMessage("Cannot resolve relation 'sys.nodes'"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(dummySources); resolver.resolveField(newQN("sys.nodes.name"), Operation.READ); } @Test public void testRegularColumnUnknown() throws Exception { expectedException.expect(ColumnUnknownException.class); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(dummySources); resolver.resolveField(newQN("age"), Operation.READ); } @Test public void testResolveDynamicReference() throws Exception { expectedException.expect(ColumnUnknownException.class); expectedException.expectMessage("Column age unknown"); AnalyzedRelation barT = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of(newQN("bar.t"), barT)); resolver.resolveField(newQN("t.age"), Operation.READ); } @Test public void testMultipleSourcesWithDynamicReferenceAndReference() throws Exception { AnalyzedRelation barT = new DummyRelation("name"); AnalyzedRelation fooT = new DummyRelation("name"); AnalyzedRelation fooA = new DummyRelation("name"); AnalyzedRelation customT = new DummyRelation("tags"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of( newQN("bar.t"), barT, newQN("foo.t"), fooT, newQN("foo.a"), fooA, newQN("custom.t"), customT)); Field field = resolver.resolveField(newQN("foo.t.name"), Operation.READ); assertThat(field.relation(), equalTo(fooT)); // reference > dynamicReference - not ambiguous Field tags = resolver.resolveField(newQN("tags"), Operation.READ); assertThat(tags.relation(), equalTo(customT)); field = resolver.resolveField(newQN("a.name"), Operation.READ); assertThat(field.relation(), equalTo(fooA)); } @Test public void testRelationOutputFromAlias() throws Exception { // t.name from doc.foo t AnalyzedRelation relation = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of( new QualifiedName(Arrays.asList("t")), relation)); Field field = resolver.resolveField(newQN("t.name"), Operation.READ); assertThat(field.relation(), equalTo(relation)); assertThat(field.path().outputName(), is("name")); } @Test public void testRelationOutputFromSingleColumnName() throws Exception { // select name from t AnalyzedRelation relation = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of(newQN("doc.t"), relation)); Field field = resolver.resolveField(newQN("name"), Operation.READ); assertThat(field.relation(), equalTo(relation)); assertThat(field.path().outputName(), is("name")); } @Test public void testRelationOutputFromSchemaTableColumnName() throws Exception { // doc.t.name from t.name AnalyzedRelation relation = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of(newQN("doc.t"), relation)); Field field = resolver.resolveField(newQN("doc.t.name"), Operation.INSERT); assertThat(field.relation(), equalTo(relation)); assertThat(field.path().outputName(), is("name")); } @Test public void testTooManyParts() throws Exception { expectedException.expect(IllegalArgumentException.class); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(dummySources); resolver.resolveField(new QualifiedName(Arrays.asList("a", "b", "c", "d")), Operation.READ); } @Test public void testTooManyPartsNameFieldResolver() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Column reference \"a.b\" has too many parts. A column must not have a schema or a table here."); FieldProvider<Field> resolver = new NameFieldProvider(dummyRelation); resolver.resolveField(new QualifiedName(Arrays.asList("a", "b")), Operation.READ); } @Test public void testRelationFromTwoTablesWithSameNameDifferentSchemaIsAmbiguous() throws Exception { // select t.name from custom.t.name, doc.t.name expectedException.expect(AmbiguousColumnException.class); expectedException.expectMessage("Column \"name\" is ambiguous"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider( ImmutableMap.<QualifiedName, AnalyzedRelation>of( new QualifiedName(Arrays.asList("custom", "t")), new DummyRelation("name"), new QualifiedName(Arrays.asList("doc", "t")), new DummyRelation("name")) ); resolver.resolveField(new QualifiedName(Arrays.asList("t", "name")), Operation.READ); } @Test public void testRelationFromTwoTables() throws Exception { // select name from doc.t, custom.t FieldProvider<Field> resolver = new FullQualifedNameFieldProvider( ImmutableMap.<QualifiedName, AnalyzedRelation>of( new QualifiedName(Arrays.asList("custom", "t")), new DummyRelation("address"), new QualifiedName(Arrays.asList("doc", "t")), new DummyRelation("name")) ); resolver.resolveField(new QualifiedName(Arrays.asList("t", "name")), Operation.READ); } @Test public void testSimpleFieldResolver() throws Exception { // select name from doc.t AnalyzedRelation relation = new DummyRelation("name"); FieldProvider<Field> resolver = new NameFieldProvider(relation); Field field = resolver.resolveField(new QualifiedName(Arrays.asList("name")), Operation.READ); assertThat(field.relation(), equalTo(relation)); } @Test public void testSimpleResolverUnknownColumn() throws Exception { expectedException.expect(ColumnUnknownException.class); expectedException.expectMessage("Column unknown unknown"); AnalyzedRelation relation = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of(newQN("doc.t"), relation)); resolver.resolveField(new QualifiedName(Arrays.asList("unknown")), Operation.READ); } @Test public void testColumnSchemaResolver() throws Exception { AnalyzedRelation barT = new DummyRelation("\"Name\""); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of( newQN("\"Bar\""), barT)); Field field = resolver.resolveField(newQN("\"Foo\".\"Bar\".\"Name\""), Operation.READ); assertThat(field.relation(), equalTo(barT)); } @Test public void testColumnSchemaResolverFail() throws Exception { expectedException.expect(ColumnUnknownException.class); expectedException.expectMessage("Column name unknown"); AnalyzedRelation barT = new DummyRelation("\"Name\""); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of( newQN("bar"), barT)); resolver.resolveField(newQN("bar.name"), Operation.READ); } @Test public void testAliasRelationNameResolver() throws Exception { AnalyzedRelation barT = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of( newQN("\"Bar\""), barT)); Field field = resolver.resolveField(newQN("\"Bar\".name"), Operation.READ); assertThat(field.relation(), equalTo(barT)); } @Test public void testAliasRelationNameResolverFail() throws Exception { expectedException.expect(RelationUnknownException.class); expectedException.expectMessage("Cannot resolve relation '\"Bar\"'"); AnalyzedRelation barT = new DummyRelation("name"); FieldProvider<Field> resolver = new FullQualifedNameFieldProvider(ImmutableMap.of( newQN("bar"), barT)); resolver.resolveField(newQN("\"Bar\".name"), Operation.READ); } }