/* * 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; import io.crate.Version; import io.crate.action.sql.Option; import io.crate.action.sql.SessionContext; import io.crate.data.Row; import io.crate.exceptions.*; import io.crate.metadata.ColumnIdent; import io.crate.metadata.FulltextAnalyzerResolver; import io.crate.metadata.IndexMappings; import io.crate.metadata.TableIdent; import io.crate.metadata.table.ColumnPolicy; import io.crate.sql.parser.SqlParser; import io.crate.test.integration.CrateDummyClusterServiceUnitTest; import io.crate.testing.SQLExecutor; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.LocalTransportAddress; import org.elasticsearch.test.ClusterServiceUtils; import org.junit.Before; import org.junit.Test; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import static com.carrotsearch.randomizedtesting.RandomizedTest.$; import static io.crate.testing.TestingHelpers.mapToSortedString; import static org.hamcrest.Matchers.*; public class CreateAlterTableStatementAnalyzerTest extends CrateDummyClusterServiceUnitTest { private SQLExecutor e; @Before public void prepare() { String analyzerSettings = FulltextAnalyzerResolver.encodeSettings( Settings.builder().put("search", "foobar").build()).utf8ToString(); MetaData metaData = MetaData.builder() .persistentSettings( Settings.builder().put("crate.analysis.custom.analyzer.ft_search", analyzerSettings).build()) .build(); ClusterState state = ClusterState.builder(ClusterName.DEFAULT) .nodes(DiscoveryNodes.builder() .add(new DiscoveryNode("n1", LocalTransportAddress.buildUnique(), org.elasticsearch.Version.CURRENT)) .add(new DiscoveryNode("n2", LocalTransportAddress.buildUnique(), org.elasticsearch.Version.CURRENT)) .add(new DiscoveryNode("n3",LocalTransportAddress.buildUnique(), org.elasticsearch.Version.CURRENT)) .localNodeId("n1") ) .metaData(metaData) .build(); ClusterServiceUtils.setState(clusterService, state); e = SQLExecutor.builder(clusterService).enableDefaultTables().build(); } @Test public void testCreateTableInSystemSchemas() throws Exception { for (String schema : CreateTableStatementAnalyzer.READ_ONLY_SCHEMAS) { try { e.analyze(String.format("CREATE TABLE %s.%s (ordinal INTEGER, name STRING)", schema, "my_table")); } catch (IllegalArgumentException e) { assertThat(e.getLocalizedMessage(), startsWith("Cannot create table in read-only schema")); } } } @Test public void testCreateTableWithAlternativePrimaryKeySyntax() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer, name string, primary key (id, name))" ); String[] primaryKeys = analysis.primaryKeys().toArray(new String[0]); assertThat(primaryKeys.length, is(2)); assertThat(primaryKeys[0], is("id")); assertThat(primaryKeys[1], is("name")); } @Test @SuppressWarnings("unchecked") public void testSimpleCreateTable() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, name string not null) " + "clustered into 3 shards with (number_of_replicas=0)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_SHARDS), is("3")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_REPLICAS), is("0")); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); assertNull(metaMapping.get("columns")); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> idMapping = (Map<String, Object>) mappingProperties.get("id"); assertThat(idMapping.get("type"), is("integer")); Map<String, Object> nameMapping = (Map<String, Object>) mappingProperties.get("name"); assertThat(nameMapping.get("type"), is("keyword")); String[] primaryKeys = analysis.primaryKeys().toArray(new String[0]); assertThat(primaryKeys.length, is(1)); assertThat(primaryKeys[0], is("id")); String[] notNullColumns = analysis.notNullColumns().toArray(new String[0]); assertThat(notNullColumns.length, is(1)); assertThat(notNullColumns[0], is("name")); } @Test public void testCreateTableWithDefaultNumberOfShards() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze("create table foo (id integer primary key, name string)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_SHARDS), is("6")); } @Test public void testCreateTableWithDefaultNumberOfShardsWithClusterByClause() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key) clustered by (id)" ); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_SHARDS), is("6")); } @Test public void testCreateTableNumberOfShardsProvidedInClusteredClause() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key) " + "clustered by (id) into 8 shards" ); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_SHARDS), is("8")); } @Test public void testCreateTableWithRefreshInterval() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "CREATE TABLE foo (id int primary key, content string) " + "with (refresh_interval=5000)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.REFRESH_INTERVAL), is("5000ms")); } @Test(expected = IllegalArgumentException.class) public void testCreateTableWithRefreshIntervalWrongNumberFormat() throws Exception { e.analyze("CREATE TABLE foo (id int primary key, content string) " + "with (refresh_interval='1asdf')"); } @Test public void testAlterTableWithRefreshInterval() throws Exception { // alter t set AlterTableAnalyzedStatement analysisSet = e.analyze( "ALTER TABLE user_refresh_interval " + "SET (refresh_interval = '5000')"); assertEquals("5000ms", analysisSet.tableParameter().settings().get(TableParameterInfo.REFRESH_INTERVAL)); // alter t reset AlterTableAnalyzedStatement analysisReset = e.analyze( "ALTER TABLE user_refresh_interval " + "RESET (refresh_interval)"); assertEquals("1000ms", analysisReset.tableParameter().settings().get(TableParameterInfo.REFRESH_INTERVAL)); } @Test public void testAlterTableWithColumnPolicy() throws Exception { AlterTableAnalyzedStatement analysisSet = e.analyze( "ALTER TABLE user_refresh_interval " + "SET (column_policy = 'strict')"); assertEquals(ColumnPolicy.STRICT.mappingValue(), analysisSet.tableParameter().mappings().get(TableParameterInfo.COLUMN_POLICY)); } @Test public void testAlterTableWithInvalidColumnPolicy() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Invalid value for argument 'column_policy'"); e.analyze("ALTER TABLE user_refresh_interval " + "SET (column_policy = 'ignored')"); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithClusteredBy() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer, name string) clustered by(id)"); Map<String, Object> meta = (Map) analysis.mapping().get("_meta"); assertNotNull(meta); assertThat((String) meta.get("routing"), is("id")); } @Test(expected = IllegalArgumentException.class) @SuppressWarnings("unchecked") public void testCreateTableWithClusteredByNotInPrimaryKeys() throws Exception { e.analyze("create table foo (id integer primary key, name string) clustered by(name)"); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithObjects() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, details object as (name string, age integer))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> details = (Map<String, Object>) mappingProperties.get("details"); assertThat((String) details.get("type"), is("object")); assertThat((String) details.get("dynamic"), is("true")); Map<String, Object> detailsProperties = (Map<String, Object>) details.get("properties"); Map<String, Object> nameProperties = (Map<String, Object>) detailsProperties.get("name"); assertThat((String) nameProperties.get("type"), is("keyword")); Map<String, Object> ageProperties = (Map<String, Object>) detailsProperties.get("age"); assertThat((String) ageProperties.get("type"), is("integer")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithStrictObject() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, details object(strict) as (name string, age integer))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> details = (Map<String, Object>) mappingProperties.get("details"); assertThat((String) details.get("type"), is("object")); assertThat((String) details.get("dynamic"), is("strict")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithIgnoredObject() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, details object(ignored))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> details = (Map<String, Object>) mappingProperties.get("details"); assertThat((String) details.get("type"), is("object")); assertThat((String) details.get("dynamic"), is("false")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithSubscriptInFulltextIndexDefinition() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table my_table1g (" + "title string, " + "author object(dynamic) as ( " + "name string, " + "birthday timestamp " + "), " + "INDEX author_title_ft using fulltext(title, author['name']))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> details = (Map<String, Object>) mappingProperties.get("author"); Map<String, Object> nameMapping = (Map<String, Object>) ((Map<String, Object>) details.get("properties")).get("name"); assertThat(((List<String>) nameMapping.get("copy_to")).get(0), is("author_title_ft")); } @Test(expected = ColumnUnknownException.class) public void testCreateTableWithInvalidFulltextIndexDefinition() throws Exception { e.analyze("create table my_table1g (" + "title string, " + "author object(dynamic) as ( " + "name string, " + "birthday timestamp " + "), " + "INDEX author_title_ft using fulltext(title, author['name']['foo']['bla']))"); } @Test public void testCreateTableWithArray() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, details array(string))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> details = (Map<String, Object>) mappingProperties.get("details"); assertThat((String) details.get("type"), is("array")); Map<String, Object> inner = (Map<String, Object>) details.get("inner"); assertThat((String) inner.get("type"), is("keyword")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithObjectsArray() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, details array(object as (name string, age integer, tags array(string))))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); assertThat(mapToSortedString(mappingProperties), is("details={inner={dynamic=true, properties={age={type=integer}, name={type=keyword}, " + "tags={inner={type=keyword}, type=array}}, type=object}, type=array}, id={type=integer}")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithAnalyzer() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, content string INDEX using fulltext with (analyzer='german'))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> contentMapping = (Map<String, Object>) mappingProperties.get("content"); assertThat(contentMapping.get("index"), nullValue()); assertThat(contentMapping.get("analyzer"), is("german")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithAnalyzerParameter() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, content string INDEX using fulltext with (analyzer=?))", new Object[]{"german"} ); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> contentMapping = (Map<String, Object>) mappingProperties.get("content"); assertThat(contentMapping.get("index"), nullValue()); assertThat(contentMapping.get("analyzer"), is("german")); } @Test public void textCreateTableWithCustomAnalyzerInNestedColumn() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table ft_search (" + "user object (strict) as (" + "name string index using fulltext with (analyzer='ft_search') " + ")" + ")"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> details = (Map<String, Object>) mappingProperties.get("user"); Map<String, Object> nameMapping = (Map<String, Object>) ((Map<String, Object>) details.get("properties")).get("name"); assertThat(nameMapping.get("index"), nullValue()); assertThat(nameMapping.get("analyzer"), is("ft_search")); assertThat(analysis.tableParameter().settings().get("search"), is("foobar")); } @Test public void testCreateTableWithSchemaName() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze("create table something.foo (id integer primary key)"); TableIdent tableIdent = analysis.tableIdent(); assertThat(tableIdent.schema(), is("something")); assertThat(tableIdent.name(), is("foo")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithIndexColumn() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, content string, INDEX content_ft using fulltext (content))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> contentMapping = (Map<String, Object>) mappingProperties.get("content"); assertThat((String) contentMapping.get("index"), isEmptyOrNullString()); assertThat(((List<String>) contentMapping.get("copy_to")).get(0), is("content_ft")); Map<String, Object> ft_mapping = (Map<String, Object>) mappingProperties.get("content_ft"); assertThat(ft_mapping.get("index"), nullValue()); assertThat(ft_mapping.get("analyzer"), is("standard")); } @Test @SuppressWarnings("unchecked") public void testCreateTableWithPlainIndexColumn() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, content string, INDEX content_ft using plain (content))"); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> contentMapping = (Map<String, Object>) mappingProperties.get("content"); assertThat((String) contentMapping.get("index"), isEmptyOrNullString()); assertThat(((List<String>) contentMapping.get("copy_to")).get(0), is("content_ft")); Map<String, Object> ft_mapping = (Map<String, Object>) mappingProperties.get("content_ft"); assertThat(ft_mapping.get("index"), nullValue()); assertThat(ft_mapping.get("analyzer"), is("keyword")); } @Test public void testCreateTableWithIndexColumnOverNonString() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("INDEX definition only support 'string' typed source columns"); e.analyze("create table foo (id integer, id2 integer, INDEX id_ft using fulltext (id, id2))"); } @Test public void testCreateTableWithIndexColumnOverNonString2() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("INDEX definition only support 'string' typed source columns"); e.analyze("create table foo (id integer, name string, INDEX id_ft using fulltext (id, name))"); } @Test public void testChangeNumberOfReplicas() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (number_of_replicas=2)"); assertThat(analysis.table().ident().name(), is("users")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_REPLICAS), is("2")); } @Test public void testResetNumberOfReplicas() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users reset (number_of_replicas)"); assertThat(analysis.table().ident().name(), is("users")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_REPLICAS), is("1")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.AUTO_EXPAND_REPLICAS), is("false")); } @Test(expected = IllegalArgumentException.class) public void testAlterTableWithInvalidProperty() throws Exception { e.analyze("alter table users set (foobar='2')"); } @Test public void testAlterSystemTable() throws Exception { expectedException.expect(UnsupportedOperationException.class); expectedException.expectMessage("The relation \"sys.shards\" doesn't support or allow ALTER " + "operations, as it is read-only."); e.analyze("alter table sys.shards reset (number_of_replicas)"); } @Test public void testCreateTableWithMultiplePrimaryKeys() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table test (id integer primary key, name string primary key)"); String[] primaryKeys = analysis.primaryKeys().toArray(new String[0]); assertThat(primaryKeys.length, is(2)); assertThat(primaryKeys[0], is("id")); assertThat(primaryKeys[1], is("name")); } @Test public void testCreateTableWithMultiplePrimaryKeysAndClusteredBy() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table test (id integer primary key, name string primary key) " + "clustered by(name)"); String[] primaryKeys = analysis.primaryKeys().toArray(new String[0]); assertThat(primaryKeys.length, is(2)); assertThat(primaryKeys[0], is("id")); assertThat(primaryKeys[1], is("name")); Map<String, Object> meta = (Map) analysis.mapping().get("_meta"); assertNotNull(meta); assertThat((String) meta.get("routing"), is("name")); } @Test(expected = IllegalArgumentException.class) public void testCreateTableWithSystemColumnPrefix() throws Exception { e.analyze("create table test (_id integer, name string)"); } @Test(expected = InvalidTableNameException.class) public void testCreateTableIllegalTableName() throws Exception { e.analyze("create table \"abc.def\" (id integer primary key, name string)"); } @Test public void testTableStartWithUnderscore() throws Exception { expectedException.expect(InvalidTableNameException.class); expectedException.expectMessage("table name \"_invalid\" is invalid."); e.analyze("create table _invalid (id integer primary key)"); } @Test public void testHasColumnDefinition() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table my_table (" + " id integer primary key, " + " name string, " + " indexed string index using fulltext with (analyzer='german')," + " arr array(object as(" + " nested float," + " nested_object object as (id byte)" + " ))," + " obj object as ( content string )," + " index ft using fulltext(name, obj['content']) with (analyzer='standard')" + ")"); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("id"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("name"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("indexed"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("arr"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("arr.nested"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("arr.nested_object.id"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("obj"))); assertTrue(analysis.hasColumnDefinition(ColumnIdent.fromPath("obj.content"))); assertFalse(analysis.hasColumnDefinition(ColumnIdent.fromPath("arr.nested.wrong"))); assertFalse(analysis.hasColumnDefinition(ColumnIdent.fromPath("ft"))); assertFalse(analysis.hasColumnDefinition(ColumnIdent.fromPath("obj.content.ft"))); } @Test public void testCreateTableWithGeoPoint() throws Exception { CreateTableAnalyzedStatement analyze = e.analyze( "create table geo_point_table (\n" + " id integer primary key,\n" + " my_point geo_point\n" + ")\n"); Map my_point = (Map) analyze.mappingProperties().get("my_point"); assertEquals("geo_point", my_point.get("type")); } @Test(expected = IllegalArgumentException.class) public void testClusteredIntoZeroShards() throws Exception { e.analyze("create table my_table (" + " id integer," + " name string" + ") clustered into 0 shards"); } @Test(expected = IllegalArgumentException.class) public void testBlobTableClusteredIntoZeroShards() throws Exception { e.analyze("create blob table my_table " + "clustered into 0 shards"); } @Test public void testEarlyPrimaryKeyConstraint() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table my_table (" + "primary key (id1, id2)," + "id1 integer," + "id2 long" + ")"); assertThat(analysis.primaryKeys().size(), is(2)); assertThat(analysis.primaryKeys(), hasItems("id1", "id2")); } @Test(expected = ColumnUnknownException.class) public void testPrimaryKeyConstraintNonExistingColumns() throws Exception { e.analyze("create table my_table (" + "primary key (id1, id2)," + "title string," + "name string" + ")"); } @Test public void testEarlyIndexDefinition() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table my_table (" + "index ft using fulltext(title, name) with (analyzer='snowball')," + "title string," + "name string" + ")"); Map<String, Object> metaMap = (Map) analysis.mapping().get("_meta"); assertThat( metaMap.get("indices").toString(), is("{ft={}}")); assertThat( (List<String>) ((Map<String, Object>) analysis.mappingProperties() .get("title")).get("copy_to"), hasItem("ft") ); assertThat( (List<String>) ((Map<String, Object>) analysis.mappingProperties() .get("name")).get("copy_to"), hasItem("ft")); } @Test(expected = ColumnUnknownException.class) public void testIndexDefinitionNonExistingColumns() throws Exception { e.analyze("create table my_table (" + "index ft using fulltext(id1, id2) with (analyzer='snowball')," + "title string," + "name string" + ")"); } @Test(expected = IllegalArgumentException.class) public void testAnalyzerOnInvalidType() throws Exception { e.analyze("create table my_table (x integer INDEX using fulltext with (analyzer='snowball'))"); } @Test public void createTableNegativeReplicas() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table t (id int, name string) with (number_of_replicas=-1)"); assertThat(analysis.tableParameter().settings().getAsInt(TableParameterInfo.NUMBER_OF_REPLICAS, 0), is(-1)); } @Test(expected = IllegalArgumentException.class) public void testCreateTableSameColumn() throws Exception { e.analyze("create table my_table (title string, title integer)"); } @Test(expected = UnsupportedOperationException.class) public void testCreateTableWithArrayPrimaryKeyUnsupported() throws Exception { e.analyze("create table t (id array(int) primary key)"); } @Test public void testCreateTableWithClusteredIntoShardsParameter() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table t (id int primary key) clustered into ? shards", new Object[]{2}); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.NUMBER_OF_SHARDS), is("2")); } @Test public void testCreateTableWithClusteredIntoShardsParameterNonNumeric() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("invalid number 'foo'"); e.analyze("create table t (id int primary key) clustered into ? shards", new Object[]{"foo"}); } @Test public void testCreateTableWithParitionedColumnInClusteredBy() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("Cannot use CLUSTERED BY column in PARTITIONED BY clause"); e.analyze("create table t(id int primary key) partitioned by (id) clustered by (id)"); } @Test public void testCreateTableWithEmptySchema() throws Exception { expectedException.expect(InvalidSchemaNameException.class); expectedException.expectMessage("schema name \"\" is invalid."); e.analyze("create table \"\".my_table (" + "id long primary key" + ")"); } @Test public void testCreateTableWithIllegalSchema() throws Exception { expectedException.expect(InvalidSchemaNameException.class); expectedException.expectMessage("schema name \"with.\" is invalid."); e.analyze("create table \"with.\".my_table (" + "id long primary key" + ")"); } @Test public void testCreateTableWithInvalidColumnName() throws Exception { expectedException.expect(InvalidColumnNameException.class); expectedException.expectMessage("column name \"'test\" is invalid"); e.analyze("create table my_table (\"'test\" string)"); } @Test public void testInvalidColumnNamePredicate() throws Exception { assertThat(ColumnIdent.INVALID_COLUMN_NAME_PREDICATE.apply("validName"), is(false)); assertThat(ColumnIdent.INVALID_COLUMN_NAME_PREDICATE.apply("invalid["), is(true)); assertThat(ColumnIdent.INVALID_COLUMN_NAME_PREDICATE.apply("invalid'"), is(true)); assertThat(ColumnIdent.INVALID_COLUMN_NAME_PREDICATE.apply("invalid]"), is(true)); assertThat(ColumnIdent.INVALID_COLUMN_NAME_PREDICATE.apply("invalid."), is(true)); } @Test public void testCreateTableShouldRaiseErrorIfItExists() throws Exception { expectedException.expect(TableAlreadyExistsException.class); e.analyze("create table users (\"'test\" string)"); } @Test public void testExplicitSchemaHasPrecedenceOverDefaultSchema() throws Exception { CreateTableAnalyzedStatement statement = (CreateTableAnalyzedStatement) e.analyzer.boundAnalyze( SqlParser.createStatement("create table foo.bar (x string)"), new SessionContext(0, Option.NONE, "hoschi", null), new ParameterContext(Row.EMPTY, Collections.<Row>emptyList())).analyzedStatement(); // schema from statement must take precedence assertThat(statement.tableIdent().schema(), is("foo")); } @Test public void testDefaultSchemaIsAddedToTableIdentIfNoEplicitSchemaExistsInTheStatement() throws Exception { CreateTableAnalyzedStatement statement = (CreateTableAnalyzedStatement) e.analyzer.boundAnalyze( SqlParser.createStatement("create table bar (x string)"), new SessionContext(0, Option.NONE, "hoschi", null), new ParameterContext(Row.EMPTY, Collections.<Row>emptyList())).analyzedStatement(); assertThat(statement.tableIdent().schema(), is("hoschi")); } @Test public void testChangeReadBlock() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"blocks.read\"=true)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.BLOCKS_READ), is("true")); } @Test public void testChangeWriteBlock() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"blocks.write\"=true)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.BLOCKS_WRITE), is("true")); } @Test public void testChangeMetadataBlock() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"blocks.metadata\"=true)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.BLOCKS_METADATA), is("true")); } @Test public void testChangeReadOnlyBlock() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"blocks.read_only\"=true)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.READ_ONLY), is("true")); } @Test public void testChangeFlushThresholdSize() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"translog.flush_threshold_size\"=300)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.FLUSH_THRESHOLD_SIZE), is("300b")); } @Test public void testChangeTranslogInterval() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"translog.sync_interval\"=50)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.TRANSLOG_SYNC_INTERVAL), is("50ms")); } @Test public void testRoutingAllocationEnable() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"routing.allocation.enable\"=\"none\")"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.ROUTING_ALLOCATION_ENABLE), is("none")); } @Test public void testRoutingAllocationValidation() throws Exception { expectedException.expect(IllegalArgumentException.class); e.analyze("alter table users set (\"routing.allocation.enable\"=\"foo\")"); } @Test public void testRecoveryShardsWithString() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"recovery.initial_shards\"=\"full\")"); assertThat(analysis.table().ident().name(), is("users")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.RECOVERY_INITIAL_SHARDS), is("full")); } @Test public void testRecoveryShardsWithInteger() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"recovery.initial_shards\"=1)"); assertThat(analysis.table().ident().name(), is("users")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.RECOVERY_INITIAL_SHARDS), is("1")); } @Test public void testTranslogSyncInterval() throws Exception { AlterTableAnalyzedStatement analysis = e.analyze("alter table users set (\"translog.sync_interval\"='1s')"); assertThat(analysis.table().ident().name(), is("users")); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.TRANSLOG_SYNC_INTERVAL), is("1000ms")); } @Test public void testRecoveryShardsValidation() throws Exception { expectedException.expect(IllegalArgumentException.class); e.analyze("alter table users set (\"recovery.initial_shards\"=\"foo\")"); } @Test public void testCreateReadOnlyTable() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (id integer primary key, name string) " + "clustered into 3 shards with (\"blocks.read_only\"=true)"); assertThat(analysis.tableParameter().settings().get(TableParameterInfo.READ_ONLY), is("true")); } @Test public void testCreateTableWithGeneratedColumn() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (ts timestamp, day as date_trunc('day', ts))"); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); Map<String, String> generatedColumnsMapping = (Map<String, String>) metaMapping.get("generated_columns"); assertThat(generatedColumnsMapping.size(), is(1)); assertThat(generatedColumnsMapping.get("day"), is("date_trunc('day', ts)")); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> dayMapping = (Map<String, Object>) mappingProperties.get("day"); assertThat((String) dayMapping.get("type"), is("date")); Map<String, Object> tsMapping = (Map<String, Object>) mappingProperties.get("ts"); assertThat((String) tsMapping.get("type"), is("date")); } @Test public void testCreateTableGeneratedColumnWithCast() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (ts timestamp, day timestamp GENERATED ALWAYS as ts + 1)"); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); Map<String, String> generatedColumnsMapping = (Map<String, String>) metaMapping.get("generated_columns"); assertThat(generatedColumnsMapping.get("day"), is("cast((ts + 1) AS timestamp)")); Map<String, Object> mappingProperties = analysis.mappingProperties(); Map<String, Object> dayMapping = (Map<String, Object>) mappingProperties.get("day"); assertThat((String) dayMapping.get("type"), is("date")); } @Test public void testCreateTableWithCurrentTimestampAsGeneratedColumnIsntNormalized() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (ts timestamp GENERATED ALWAYS as current_timestamp)"); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); Map<String, String> generatedColumnsMapping = (Map<String, String>) metaMapping.get("generated_columns"); assertThat(generatedColumnsMapping.size(), is(1)); // current_timestamp used to get evaluated and then this contained the actual timestamp instead of the function name assertThat(generatedColumnsMapping.get("ts"), is("current_timestamp(3)")); // 3 is the default precision } @Test public void testCreateTableGeneratedColumnWithSubscript() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (user object as (name string), name as concat(user['name'], 'foo'))"); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); Map<String, String> generatedColumnsMapping = (Map<String, String>) metaMapping.get("generated_columns"); assertThat(generatedColumnsMapping.get("name"), is("concat(user['name'], 'foo')")); } @Test public void testCreateTableGeneratedColumnParameter() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table foo (user object as (name string), name as concat(user['name'], ?))", $("foo")); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); Map<String, String> generatedColumnsMapping = (Map<String, String>) metaMapping.get("generated_columns"); assertThat(generatedColumnsMapping.get("name"), is("concat(user['name'], 'foo')")); } @Test public void testCreateTableGeneratedColumnWithInvalidType() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("generated expression value type 'timestamp' not supported for conversion to 'string'"); e.analyze("create table foo (ts timestamp, day string GENERATED ALWAYS as date_trunc('day', ts))"); } @Test public void testCreateTableGeneratedColumnWithMatch() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("can only MATCH on columns, not on name"); e.analyze("create table foo (name string, bar as match(name, 'crate'))"); } @Test public void testCreateTableGeneratedColumnBasedOnGeneratedColumn() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("A generated column cannot be based on a generated column"); e.analyze("create table foo (ts timestamp, day as date_trunc('day', ts), date_string as cast(day as string))"); } @Test public void testCreateTableGeneratedColumnBasedOnUnknownColumn() throws Exception { expectedException.expect(ColumnUnknownException.class); expectedException.expectMessage("Column unknown_col unknown"); e.analyze("create table foo (ts timestamp, day as date_trunc('day', ts), date_string as cast(unknown_col as string))"); } @Test public void testCreateTableWithObjectAsPrimaryKey() throws Exception { expectedException.expectMessage("Cannot use columns of type \"object\" as primary key"); expectedException.expect(UnsupportedOperationException.class); e.analyze("create table t (obj object as (x int) primary key)"); } @Test public void testCreateTableWithGeoPointAsPrimaryKey() throws Exception { expectedException.expectMessage("Cannot use columns of type \"geo_point\" as primary key"); expectedException.expect(UnsupportedOperationException.class); e.analyze("create table t (c geo_point primary key)"); } @Test public void testCreateTableWithGeoShapeAsPrimaryKey() throws Exception { expectedException.expectMessage("Cannot use columns of type \"geo_shape\" as primary key"); expectedException.expect(UnsupportedOperationException.class); e.analyze("create table t (c geo_shape primary key)"); } @Test public void testCreateTableWithDuplicatePrimaryKey() throws Exception { assertDuplicatePrimaryKey("create table t (id int, primary key (id, id))"); assertDuplicatePrimaryKey("create table t (obj object as (id int), primary key (obj['id'], obj['id']))"); assertDuplicatePrimaryKey("create table t (id int primary key, primary key (id))"); assertDuplicatePrimaryKey("create table t (obj object as (id int primary key), primary key (obj['id']))"); } private void assertDuplicatePrimaryKey(String stmt) throws Exception { try { e.analyze(stmt); fail(String.format(Locale.ENGLISH, "Statement '%s' did not result in duplicate primary key exception", stmt)); } catch (IllegalArgumentException e) { String msg = "appears twice in primary key constraint"; if (!e.getMessage().contains(msg)) { fail("Exception message is expected to contain: " + msg); } } } @Test public void testCreateTableWithPrimaryKeyConstraintInArrayItem() throws Exception { expectedException.expect(UnsupportedOperationException.class); expectedException.expectMessage("Cannot use column \"id\" as primary key within an array object"); e.analyze("create table test (arr array(object as (id long primary key)))"); } @Test public void testCreateTableWithDeepNestedPrimaryKeyConstraintInArrayItem() throws Exception { expectedException.expect(UnsupportedOperationException.class); expectedException.expectMessage("Cannot use column \"name\" as primary key within an array object"); e.analyze("create table test (arr array(object as (user object as (name string primary key), id long)))"); } @Test public void testCreateTableWithInvalidIndexConstraint() throws Exception { expectedException.expect(IllegalArgumentException.class); expectedException.expectMessage("INDEX constraint cannot be used on columns of type \"object\""); e.analyze("create table test (obj object index off)"); } @Test public void testCreateTableDefaultRoutingHashFunctionSet() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table default_routing_hash_set (id int)"); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); assertThat(metaMapping.get(IndexMappings.SETTING_ROUTING_HASH_FUNCTION), is(IndexMappings.DEFAULT_ROUTING_HASH_FUNCTION)); } @Test public void testCreateTableCreatedVersionSet() throws Exception { CreateTableAnalyzedStatement analysis = e.analyze( "create table created_version_table (id int)"); Map<String, Object> metaMapping = ((Map) analysis.mapping().get("_meta")); Map<String, Object> versionMap = (Map) metaMapping.get("version"); assertThat(versionMap.get(Version.Property.CREATED.toString()), is(Version.toMap(Version.CURRENT))); assertThat(versionMap.get(Version.Property.UPGRADED.toString()), nullValue()); } }