blob: 8e9b632d10702021123db4d7f09f6974b0d799e9 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF 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.
package org.apache.impala.analysis;
import org.apache.impala.catalog.KuduTable;
import org.apache.impala.common.FrontendTestBase;
import org.apache.impala.service.BackendConfig;
import org.apache.impala.testutil.TestUtils;
import org.apache.kudu.ColumnSchema.CompressionAlgorithm;
import org.apache.kudu.ColumnSchema.Encoding;
import org.junit.Test;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import java.util.List;
/**
* Tests on DDL analysis for Kudu tables.
*/
public class AnalyzeKuduDDLTest extends FrontendTestBase {
@Test
public void TestCreateManagedKuduTable() {
TestUtils.assumeKuduIsSupported();
// Test primary keys and partition by clauses
AnalyzesOk("create table tab (x int primary key) partition by hash(x) " +
"partitions 8 stored as kudu");
AnalyzesOk("create table tab (x int, primary key(x)) partition by hash(x) " +
"partitions 8 stored as kudu");
AnalyzesOk("create table tab (x int, y int, primary key (x, y)) " +
"partition by hash(x, y) partitions 8 stored as kudu");
AnalyzesOk("create table tab (x int, y int, primary key (x)) " +
"partition by hash(x) partitions 8 stored as kudu");
AnalyzesOk("create table tab (x int, y int, primary key(x, y)) " +
"partition by hash(y) partitions 8 stored as kudu");
AnalyzesOk("create table tab (x timestamp, y timestamp, primary key(x)) " +
"partition by hash(x) partitions 8 stored as kudu");
AnalyzesOk("create table tab (x int, y string, primary key (x)) partition by " +
"hash (x) partitions 3, range (x) (partition values < 1, partition " +
"1 <= values < 10, partition 10 <= values < 20, partition value = 30) " +
"stored as kudu");
AnalyzesOk("create table tab (x int, y int, primary key (x, y)) partition by " +
"range (x, y) (partition value = (2001, 1), partition value = (2002, 1), " +
"partition value = (2003, 2)) stored as kudu");
// Non-literal boundary values in range partitions
AnalyzesOk("create table tab (x int, y int, primary key (x)) partition by " +
"range (x) (partition values < 1 + 1, partition (1+3) + 2 < values < 10, " +
"partition factorial(4) < values < factorial(5), " +
"partition value = factorial(6)) stored as kudu");
AnalyzesOk("create table tab (x int, y int, primary key(x, y)) partition by " +
"range(x, y) (partition value = (1+1, 2+2), partition value = ((1+1+1)+1, 10), " +
"partition value = (cast (30 as int), factorial(5))) stored as kudu");
AnalysisError("create table tab (x int primary key) partition by range (x) " +
"(partition values < x + 1) stored as kudu", "Only constant values are allowed " +
"for range-partition bounds: x + 1");
AnalysisError("create table tab (x int primary key) partition by range (x) " +
"(partition values <= isnull(null, null)) stored as kudu", "Range partition " +
"values cannot be NULL. Range partition: 'PARTITION VALUES <= " +
"isnull(NULL, NULL)'");
AnalysisError("create table tab (x int primary key) partition by range (x) " +
"(partition values <= (select count(*) from functional.alltypestiny)) " +
"stored as kudu", "Only constant values are allowed for range-partition " +
"bounds: (SELECT count(*) FROM functional.alltypestiny)");
// Multilevel partitioning. Data is split into 3 buckets based on 'x' and each
// bucket is partitioned into 4 tablets based on the range partitions of 'y'.
AnalyzesOk("create table tab (x int, y string, primary key(x, y)) " +
"partition by hash(x) partitions 3, range(y) " +
"(partition values < 'aa', partition 'aa' <= values < 'bb', " +
"partition 'bb' <= values < 'cc', partition 'cc' <= values) " +
"stored as kudu");
// Key column in upper case
AnalyzesOk("create table tab (x int, y int, primary key (X)) " +
"partition by hash (x) partitions 8 stored as kudu");
// Flexible Partitioning
AnalyzesOk("create table tab (a int, b int, c int, d int, primary key (a, b, c))" +
"partition by hash (a, b) partitions 8, hash(c) partitions 2 stored as " +
"kudu");
// No columns specified in the PARTITION BY HASH clause
AnalyzesOk("create table tab (a int primary key, b int, c int, d int) " +
"partition by hash partitions 8 stored as kudu");
// Distribute range data types are picked up during analysis and forwarded to Kudu.
// Column names in distribute params should also be case-insensitive.
AnalyzesOk("create table tab (a int, b int, c int, d int, primary key(a, b, c, d))" +
"partition by hash (a, B, c) partitions 8, " +
"range (A) (partition values < 1, partition 1 <= values < 2, " +
"partition 2 <= values < 3, partition 3 <= values < 4, partition 4 <= values) " +
"stored as kudu");
// Allowing range partitioning on a subset of the primary keys
AnalyzesOk("create table tab (id int, name string, valf float, vali bigint, " +
"primary key (id, name)) partition by range (name) " +
"(partition 'aa' < values <= 'bb') stored as kudu");
// Null values in range partition values
AnalysisError("create table tab (id int, name string, primary key(id, name)) " +
"partition by hash (id) partitions 3, range (name) " +
"(partition value = null, partition value = 1) stored as kudu",
"Range partition values cannot be NULL. Range partition: 'PARTITION " +
"VALUE = NULL'");
// Primary key specified in tblproperties
AnalysisError(String.format("create table tab (x int) partition by hash (x) " +
"partitions 8 stored as kudu tblproperties ('%s' = 'x')",
KuduTable.KEY_KEY_COLUMNS), "PRIMARY KEY must be used instead of the table " +
"property");
// Primary key column that doesn't exist
AnalysisError("create table tab (x int, y int, primary key (z)) " +
"partition by hash (x) partitions 8 stored as kudu",
"PRIMARY KEY column 'z' does not exist in the table");
// Invalid composite primary key
AnalysisError("create table tab (x int primary key, primary key(x)) stored " +
"as kudu", "Multiple primary keys specified. Composite primary keys can " +
"be specified using the PRIMARY KEY (col1, col2, ...) syntax at the end " +
"of the column definition.");
AnalysisError("create table tab (x int primary key, y int primary key) stored " +
"as kudu", "Multiple primary keys specified. Composite primary keys can " +
"be specified using the PRIMARY KEY (col1, col2, ...) syntax at the end " +
"of the column definition.");
// Specifying the same primary key column multiple times
AnalysisError("create table tab (x int, primary key (x, x)) partition by hash (x) " +
"partitions 8 stored as kudu",
"Column 'x' is listed multiple times as a PRIMARY KEY.");
// Number of range partition boundary values should be equal to the number of range
// columns.
AnalysisError("create table tab (a int, b int, c int, d int, primary key(a, b, c)) " +
"partition by range(a) (partition value = (1, 2), " +
"partition value = 3, partition value = 4) stored as kudu",
"Number of specified range partition values is different than the number of " +
"partitioning columns: (2 vs 1). Range partition: 'PARTITION VALUE = (1, 2)'");
// Key ranges must match the column types.
AnalysisError("create table tab (a int, b int, c int, d int, primary key(a, b, c)) " +
"partition by hash (a, b, c) partitions 8, range (a) " +
"(partition value = 1, partition value = 'abc', partition 3 <= values) " +
"stored as kudu", "Range partition value 'abc' (type: STRING) is not type " +
"compatible with partitioning column 'a' (type: INT).");
AnalysisError("create table tab (a tinyint primary key) partition by range (a) " +
"(partition value = 128) stored as kudu", "Range partition value 128 " +
"(type: SMALLINT) is not type compatible with partitioning column 'a' " +
"(type: TINYINT)");
AnalysisError("create table tab (a smallint primary key) partition by range (a) " +
"(partition value = 32768) stored as kudu", "Range partition value 32768 " +
"(type: INT) is not type compatible with partitioning column 'a' " +
"(type: SMALLINT)");
AnalysisError("create table tab (a int primary key) partition by range (a) " +
"(partition value = 2147483648) stored as kudu", "Range partition value " +
"2147483648 (type: BIGINT) is not type compatible with partitioning column 'a' " +
"(type: INT)");
AnalysisError("create table tab (a bigint primary key) partition by range (a) " +
"(partition value = 9223372036854775808) stored as kudu", "Range partition " +
"value 9223372036854775808 (type: DECIMAL(19,0)) is not type compatible with " +
"partitioning column 'a' (type: BIGINT)");
// Test implicit casting/folding of partition values.
AnalyzesOk("create table tab (a int primary key) partition by range (a) " +
"(partition value = false, partition value = true) stored as kudu");
// Non-key column used in PARTITION BY
AnalysisError("create table tab (a int, b string, c bigint, primary key (a)) " +
"partition by range (b) (partition value = 'abc') stored as kudu",
"Column 'b' in 'RANGE (b) (PARTITION VALUE = 'abc')' is not a key column. " +
"Only key columns can be used in PARTITION BY.");
// No float range partition values
AnalysisError("create table tab (a int, b int, c int, d int, primary key (a, b, c))" +
"partition by hash (a, b, c) partitions 8, " +
"range (a) (partition value = 1.2, partition value = 2) stored as kudu",
"Range partition value 1.2 (type: DECIMAL(2,1)) is not type compatible with " +
"partitioning column 'a' (type: INT).");
// Non-existing column used in PARTITION BY
AnalysisError("create table tab (a int, b int, primary key (a, b)) " +
"partition by range(unknown_column) (partition value = 'abc') stored as kudu",
"Column 'unknown_column' in 'RANGE (unknown_column) " +
"(PARTITION VALUE = 'abc')' is not a key column. Only key columns can be used " +
"in PARTITION BY");
// Kudu num_tablet_replicas is specified in tblproperties
String kuduMasters = catalog_.getDefaultKuduMasterHosts();
AnalyzesOk(String.format("create table tab (x int primary key) partition by " +
"hash (x) partitions 8 stored as kudu tblproperties " +
"('kudu.num_tablet_replicas'='1', 'kudu.master_addresses' = '%s')",
kuduMasters));
// Kudu table name is specified in tblproperties resulting in an error
AnalysisError("create table tab (x int primary key) partition by hash (x) " +
"partitions 8 stored as kudu tblproperties ('kudu.table_name'='tab')",
"Not allowed to set 'kudu.table_name' manually for managed Kudu tables");
// No port is specified in kudu master address
AnalyzesOk(String.format("create table tdata_no_port (id int primary key, " +
"name string, valf float, vali bigint) partition by range(id) " +
"(partition values <= 10, partition 10 < values <= 30, " +
"partition 30 < values) stored as kudu tblproperties" +
"('kudu.master_addresses' = '%s')", kuduMasters));
// Not using the STORED AS KUDU syntax to specify a Kudu table
AnalysisError("create table tab (x int) tblproperties (" +
"'storage_handler'='org.apache.hadoop.hive.kudu.KuduStorageHandler')",
CreateTableStmt.KUDU_STORAGE_HANDLER_ERROR_MESSAGE);
// Creating unpartitioned table results in a warning.
AnalyzesOk("create table tab (x int primary key) stored as kudu tblproperties (" +
"'storage_handler'='org.apache.hadoop.hive.kudu.KuduStorageHandler')",
"Unpartitioned Kudu tables are inefficient for large data sizes.");
// Invalid value for number of replicas
AnalysisError("create table t (x int primary key) stored as kudu tblproperties (" +
"'kudu.num_tablet_replicas'='1.1')",
"Table property 'kudu.num_tablet_replicas' must be an integer.");
// Don't allow caching
AnalysisError("create table tab (x int primary key) stored as kudu cached in " +
"'testPool'", "A Kudu table cannot be cached in HDFS.");
// LOCATION cannot be used with Kudu tables
AnalysisError("create table tab (a int primary key) partition by hash (a) " +
"partitions 3 stored as kudu location '/test-warehouse/'",
"LOCATION cannot be specified for a Kudu table.");
// Creating unpartitioned table results in a warning.
AnalyzesOk("create table tab (a int, primary key (a)) stored as kudu",
"Unpartitioned Kudu tables are inefficient for large data sizes.");
AnalysisError("create table tab (a int) stored as kudu",
"A primary key is required for a Kudu table.");
// Using ROW FORMAT with a Kudu table
AnalysisError("create table tab (x int primary key) " +
"row format delimited escaped by 'X' stored as kudu",
"ROW FORMAT cannot be specified for file format KUDU.");
// Using PARTITIONED BY with a Kudu table
AnalysisError("create table tab (x int primary key) " +
"partitioned by (y int) stored as kudu", "PARTITIONED BY cannot be used " +
"in Kudu tables.");
// Multi-column range partitions
AnalyzesOk("create table tab (a bigint, b tinyint, c double, primary key(a, b)) " +
"partition by range(a, b) (partition (0, 0) < values <= (1, 1)) stored as kudu");
AnalysisError("create table tab (a bigint, b tinyint, c double, primary key(a, b)) " +
"partition by range(a, b) (partition values <= (1, 'b')) stored as kudu",
"Range partition value 'b' (type: STRING) is not type compatible with " +
"partitioning column 'b' (type: TINYINT)");
AnalysisError("create table tab (a bigint, b tinyint, c double, primary key(a, b)) " +
"partition by range(a, b) (partition 0 < values <= 1) stored as kudu",
"Number of specified range partition values is different than the number of " +
"partitioning columns: (1 vs 2). Range partition: 'PARTITION 0 < VALUES <= 1'");
// Test unsupported Kudu types
List<String> unsupportedTypes = Lists.newArrayList("VARCHAR(20)", "CHAR(20)",
"STRUCT<f1:INT,f2:STRING>", "ARRAY<INT>", "MAP<STRING,STRING>");
for (String t: unsupportedTypes) {
String expectedError = String.format(
"Cannot create table 'tab': Type %s is not supported in Kudu", t);
// Unsupported type is PK and partition col
String stmt = String.format("create table tab (x %s primary key) " +
"partition by hash(x) partitions 3 stored as kudu", t);
AnalysisError(stmt, expectedError);
// Unsupported type is not PK/partition col
stmt = String.format("create table tab (x int primary key, y %s) " +
"partition by hash(x) partitions 3 stored as kudu", t);
AnalysisError(stmt, expectedError);
}
// Test column options
String[] nullability = {"not null", "null", ""};
String[] defaultVal = {"default 10", ""};
String[] blockSize = {"block_size 4096", ""};
for (Encoding enc: Encoding.values()) {
for (CompressionAlgorithm comp: CompressionAlgorithm.values()) {
for (String nul: nullability) {
for (String def: defaultVal) {
for (String block: blockSize) {
// Test analysis for a non-key column
AnalyzesOk(String.format("create table tab (x int primary key " +
"not null encoding %s compression %s %s %s, y int encoding %s " +
"compression %s %s %s %s) partition by hash (x) " +
"partitions 3 stored as kudu", enc, comp, def, block, enc,
comp, def, nul, block));
// For a key column
String createTblStr = String.format("create table tab (x int primary key " +
"%s encoding %s compression %s %s %s) partition by hash (x) " +
"partitions 3 stored as kudu", nul, enc, comp, def, block);
if (nul.equals("null")) {
AnalysisError(createTblStr, "Primary key columns cannot be nullable");
} else {
AnalyzesOk(createTblStr);
}
}
}
}
}
}
// Use NULL as default values
AnalyzesOk("create table tab (x int primary key, i1 tinyint default null, " +
"i2 smallint default null, i3 int default null, i4 bigint default null, " +
"vals string default null, valf float default null, vald double default null, " +
"valb boolean default null, valdec decimal(10, 5) default null) " +
"partition by hash (x) partitions 3 stored as kudu");
// Use NULL as a default value on a non-nullable column
AnalysisError("create table tab (x int primary key, y int not null default null) " +
"partition by hash (x) partitions 3 stored as kudu", "Default value of NULL " +
"not allowed on non-nullable column: 'y'");
// Primary key specified using the PRIMARY KEY clause
AnalyzesOk("create table tab (x int not null encoding plain_encoding " +
"compression snappy block_size 1, y int null encoding rle compression lz4 " +
"default 1, primary key(x)) partition by hash (x) partitions 3 " +
"stored as kudu");
// Primary keys can't be null
AnalysisError("create table tab (x int primary key null, y int not null) " +
"partition by hash (x) partitions 3 stored as kudu", "Primary key columns " +
"cannot be nullable: x INT PRIMARY KEY NULL");
AnalysisError("create table tab (x int not null, y int null, primary key (x, y)) " +
"partition by hash (x) partitions 3 stored as kudu", "Primary key columns " +
"cannot be nullable: y INT NULL");
// Unsupported encoding value
AnalysisError("create table tab (x int primary key, y int encoding invalid_enc) " +
"partition by hash (x) partitions 3 stored as kudu", "Unsupported encoding " +
"value 'INVALID_ENC'. Supported encoding values are: " +
Joiner.on(", ").join(Encoding.values()));
// Unsupported compression algorithm
AnalysisError("create table tab (x int primary key, y int compression " +
"invalid_comp) partition by hash (x) partitions 3 stored as kudu",
"Unsupported compression algorithm 'INVALID_COMP'. Supported compression " +
"algorithms are: " + Joiner.on(", ").join(CompressionAlgorithm.values()));
// Default values
AnalyzesOk("create table tab (i1 tinyint default 1, i2 smallint default 10, " +
"i3 int default 100, i4 bigint default 1000, vals string default 'test', " +
"valf float default cast(1.2 as float), vald double default " +
"cast(3.1452 as double), valb boolean default true, " +
"valdec decimal(10, 5) default 3.14159, " +
"primary key (i1, i2, i3, i4, vals)) partition by hash (i1) partitions 3 " +
"stored as kudu");
AnalyzesOk("create table tab (i int primary key default 1+1+1) " +
"partition by hash (i) partitions 3 stored as kudu");
AnalyzesOk("create table tab (i int primary key default factorial(5)) " +
"partition by hash (i) partitions 3 stored as kudu");
AnalyzesOk("create table tab (i int primary key, x int null default " +
"isnull(null, null)) partition by hash (i) partitions 3 stored as kudu");
// Invalid default values
AnalysisError("create table tab (i int primary key default 'string_val') " +
"partition by hash (i) partitions 3 stored as kudu", "Default value " +
"'string_val' (type: STRING) is not compatible with column 'i' (type: INT).");
AnalysisError("create table tab (i int primary key, x int default 1.1) " +
"partition by hash (i) partitions 3 stored as kudu",
"Default value 1.1 (type: DECIMAL(2,1)) is not compatible with column " +
"'x' (type: INT).");
AnalysisError("create table tab (i tinyint primary key default 128) " +
"partition by hash (i) partitions 3 stored as kudu", "Default value " +
"128 (type: SMALLINT) is not compatible with column 'i' (type: TINYINT).");
AnalysisError("create table tab (i int primary key default isnull(null, null)) " +
"partition by hash (i) partitions 3 stored as kudu", "Default value of " +
"NULL not allowed on non-nullable column: 'i'");
AnalysisError("create table tab (i int primary key, x int not null " +
"default isnull(null, null)) partition by hash (i) partitions 3 " +
"stored as kudu", "Default value of NULL not allowed on non-nullable column: " +
"'x'");
// Invalid block_size values
AnalysisError("create table tab (i int primary key block_size 1.1) " +
"partition by hash (i) partitions 3 stored as kudu", "Invalid value " +
"for BLOCK_SIZE: 1.1. A positive INTEGER value is expected.");
AnalysisError("create table tab (i int primary key block_size 'val') " +
"partition by hash (i) partitions 3 stored as kudu", "Invalid value " +
"for BLOCK_SIZE: 'val'. A positive INTEGER value is expected.");
// Sort columns are not supported for Kudu tables.
AnalysisError("create table tab (i int, x int primary key) partition by hash(x) " +
"partitions 8 sort by(i) stored as kudu", "SORT BY is not supported for Kudu " +
"tables.");
// Z-Sort columns are not supported for Kudu tables.
BackendConfig.INSTANCE.setZOrderSortUnlocked(true);
AnalysisError("create table tab (i int, x int primary key) partition by hash(x) " +
"partitions 8 sort by zorder(i) stored as kudu", "SORT BY is not " +
"supported for Kudu tables.");
BackendConfig.INSTANCE.setZOrderSortUnlocked(false);
// Range partitions with TIMESTAMP
AnalyzesOk("create table ts_ranges (ts timestamp primary key) " +
"partition by range (partition cast('2009-01-01 00:00:00' as timestamp) " +
"<= VALUES < '2009-01-02 00:00:00') stored as kudu");
AnalyzesOk("create table ts_ranges (ts timestamp primary key) " +
"partition by range (partition value = cast('2009-01-01 00:00:00' as timestamp" +
")) stored as kudu");
AnalyzesOk("create table ts_ranges (ts timestamp primary key) " +
"partition by range (partition value = '2009-01-01 00:00:00') " +
"stored as kudu");
AnalyzesOk("create table ts_ranges (id int, ts timestamp, primary key(id, ts))" +
"partition by range (partition value = (9, cast('2009-01-01 00:00:00' as " +
"timestamp))) stored as kudu");
AnalyzesOk("create table ts_ranges (id int, ts timestamp, primary key(id, ts))" +
"partition by range (partition value = (9, '2009-01-01 00:00:00')) " +
"stored as kudu");
AnalysisError("create table ts_ranges (ts timestamp primary key, i int)" +
"partition by range (partition '2009-01-01 00:00:00' <= VALUES < " +
"'NOT A TIMESTAMP') stored as kudu",
"Range partition value 'NOT A TIMESTAMP' cannot be cast to target TIMESTAMP " +
"partitioning column.");
AnalysisError("create table ts_ranges (ts timestamp primary key, i int)" +
"partition by range (partition 100 <= VALUES < 200) stored as kudu",
"Range partition value 100 (type: TINYINT) is not type " +
"compatible with partitioning column 'ts' (type: TIMESTAMP).");
// TIMESTAMP columns with default values
AnalyzesOk("create table tdefault (id int primary key, ts timestamp default now())" +
"partition by hash(id) partitions 3 stored as kudu");
AnalyzesOk("create table tdefault (id int primary key, ts timestamp default " +
"unix_micros_to_utc_timestamp(1230768000000000)) partition by hash(id) " +
"partitions 3 stored as kudu");
AnalyzesOk("create table tdefault (id int primary key, " +
"ts timestamp not null default '2009-01-01 00:00:00') " +
"partition by hash(id) partitions 3 stored as kudu");
AnalyzesOk("create table tdefault (id int primary key, " +
"ts timestamp not null default cast('2009-01-01 00:00:00' as timestamp)) " +
"partition by hash(id) partitions 3 stored as kudu");
AnalysisError("create table tdefault (id int primary key, ts timestamp " +
"default null) partition by hash(id) partitions 3 stored as kudu",
"NULL cannot be cast to a TIMESTAMP literal.");
AnalysisError("create table tdefault (id int primary key, " +
"ts timestamp not null default cast('00:00:00' as timestamp)) " +
"partition by hash(id) partitions 3 stored as kudu",
"CAST('00:00:00' AS TIMESTAMP) cannot be cast to a TIMESTAMP literal.");
AnalysisError("create table tdefault (id int primary key, " +
"ts timestamp not null default '2009-1 foo') " +
"partition by hash(id) partitions 3 stored as kudu",
"String '2009-1 foo' cannot be cast to a TIMESTAMP literal.");
// Test column comments.
AnalyzesOk("create table tab (x int comment 'x', y int comment 'y', " +
"primary key (x, y)) stored as kudu");
// Managed table is not allowed to set table property 'kudu.table_id'.
AnalysisError("create table tab (x int primary key) partition by hash(x) " +
"partitions 8 stored as kudu tblproperties ('kudu.table_id'='123456')",
String.format("Table property %s should not be specified when " +
"creating a Kudu table.", KuduTable.KEY_TABLE_ID));
// Kudu master address needs to be valid.
AnalysisError("create table tab (x int primary key) partition by " +
"hash (x) partitions 8 stored as kudu tblproperties " +
"('kudu.master_addresses' = 'foo')",
"Cannot analyze Kudu table 'tab': Error determining if Kudu's integration " +
"with the Hive Metastore is enabled");
}
@Test
public void TestCreateExternalKuduTable() {
TestUtils.assumeKuduIsSupported();
final String kuduMasters = catalog_.getDefaultKuduMasterHosts();
AnalyzesOk("create external table t stored as kudu " +
"tblproperties('kudu.table_name'='t')");
// Use all allowed optional table props.
AnalyzesOk(String.format("create external table t stored as kudu tblproperties (" +
"'kudu.table_name'='tab', 'kudu.master_addresses' = '%s', " +
"'storage_handler'='org.apache.hadoop.hive.kudu.KuduStorageHandler')",
kuduMasters));
// Kudu table should be specified using the STORED AS KUDU syntax.
AnalysisError("create external table t tblproperties (" +
"'storage_handler'='com.cloudera.kudu.hive.KuduStorageHandler'," +
"'kudu.table_name'='t')",
CreateTableStmt.KUDU_STORAGE_HANDLER_ERROR_MESSAGE);
AnalysisError("create external table t tblproperties (" +
"'storage_handler'='org.apache.hadoop.hive.kudu.KuduStorageHandler'," +
"'kudu.table_name'='t')",
CreateTableStmt.KUDU_STORAGE_HANDLER_ERROR_MESSAGE);
// Columns should not be specified in an external Kudu table
AnalysisError("create external table t (x int) stored as kudu " +
"tblproperties('kudu.table_name'='t')",
"Columns cannot be specified with an external Kudu table.");
// Primary keys cannot be specified in an external Kudu table
AnalysisError("create external table t (x int primary key) stored as kudu " +
"tblproperties('kudu.table_name'='t')", "Primary keys cannot be specified " +
"for an external Kudu table");
// Invalid syntax for specifying a Kudu table
AnalysisError("create external table t (x int) stored as parquet tblproperties (" +
"'storage_handler'='com.cloudera.kudu.hive.KuduStorageHandler'," +
"'kudu.table_name'='t')", CreateTableStmt.KUDU_STORAGE_HANDLER_ERROR_MESSAGE);
AnalysisError("create external table t (x int) stored as parquet tblproperties (" +
"'storage_handler'='org.apache.hadoop.hive.kudu.KuduStorageHandler'," +
"'kudu.table_name'='t')", CreateTableStmt.KUDU_STORAGE_HANDLER_ERROR_MESSAGE);
AnalysisError("create external table t stored as kudu tblproperties (" +
"'storage_handler'='foo', 'kudu.table_name'='t')",
"Invalid storage handler specified for Kudu table: foo");
// Cannot specify the number of replicas for external Kudu tables
AnalysisError("create external table tab (x int) stored as kudu " +
"tblproperties ('kudu.num_tablet_replicas' = '1', " +
"'kudu.table_name'='tab')",
"Table property 'kudu.num_tablet_replicas' cannot be used with an external " +
"Kudu table.");
// Don't allow caching
AnalysisError("create external table t stored as kudu cached in 'testPool' " +
"tblproperties('kudu.table_name'='t')",
"A Kudu table cannot be cached in HDFS.");
// LOCATION cannot be used for a Kudu table
AnalysisError("create external table t stored as kudu " +
"location '/test-warehouse' tblproperties('kudu.table_name'='t')",
"LOCATION cannot be specified for a Kudu table.");
// External table is not allowed to set table property 'kudu.table_id'.
AnalysisError("create external table t stored as kudu tblproperties " +
"('kudu.table_name'='t', 'kudu.table_id'='123456')",
String.format("Table property %s should not be specified when creating " +
"a Kudu table.", KuduTable.KEY_TABLE_ID));
// External table is not allowed to set table property 'external.table.purge'
// to true.
AnalysisError("create external table t stored as kudu tblproperties " +
"('external.table.purge'='true', 'kudu.table_name'='t')",
"Table property 'external.table.purge' cannot be set " +
"to true with an external Kudu table.");
}
@Test
public void TestAlterKuduTable() {
TestUtils.assumeKuduIsSupported();
// ALTER TABLE ADD/DROP range partitions
String[] addDrop = {"add if not exists", "add", "drop if exists", "drop"};
for (String kw: addDrop) {
AnalyzesOk(String.format("alter table functional_kudu.testtbl %s range " +
"partition 10 <= values < 20", kw));
AnalyzesOk(String.format("alter table functional_kudu.testtbl %s range " +
"partition value = 30", kw));
AnalyzesOk(String.format("alter table functional_kudu.testtbl %s range " +
"partition values < 100", kw));
AnalyzesOk(String.format("alter table functional_kudu.testtbl %s range " +
"partition 10 <= values", kw));
AnalyzesOk(String.format("alter table functional_kudu.testtbl %s range " +
"partition 1+1 <= values <= factorial(3)", kw));
AnalyzesOk(String.format("alter table functional_kudu.jointbl %s range " +
"partition (0, '0') < values", kw));
AnalyzesOk(String.format("alter table functional_kudu.jointbl %s range " +
"partition values <= (1, '1')", kw));
AnalyzesOk(String.format("alter table functional_kudu.jointbl %s range " +
"partition (0, '0') <= values < (1, '1')", kw));
AnalyzesOk(String.format("alter table functional_kudu.jointbl %s range " +
"partition value = (-1, 'a')", kw));
AnalysisError(String.format("alter table functional.alltypes %s range " +
"partition 10 < values < 20", kw), "Table functional.alltypes does not " +
"support range partitions: RANGE PARTITION 10 < VALUES < 20");
AnalysisError(String.format("alter table functional_kudu.testtbl %s range " +
"partition values < isnull(null, null)", kw), "Range partition values " +
"cannot be NULL. Range partition: 'PARTITION VALUES < isnull(NULL, NULL)'");
AnalysisError(String.format("alter table functional_kudu.jointbl %s range " +
"partition (0) < values", kw),
"Number of specified range partition values is different than the number of " +
"partitioning columns: (1 vs 2). Range partition: 'PARTITION (0) < VALUES'");
AnalysisError(String.format("alter table functional_kudu.jointbl %s range " +
"partition values < (0, 0)", kw),
"Range partition value 0 (type: TINYINT) is not type compatible with " +
"partitioning column 'test_name' (type: STRING).");
}
// ALTER TABLE ADD COLUMNS
// Columns with different supported data types
AnalyzesOk("alter table functional_kudu.testtbl add columns (a1 tinyint null, a2 " +
"smallint null, a3 int null, a4 bigint null, a5 string null, a6 float null, " +
"a7 double null, a8 boolean null comment 'boolean')");
// Decimal types
AnalyzesOk("alter table functional_kudu.testtbl add columns (d1 decimal null, d2 " +
"decimal(9, 2) null, d3 decimal(15, 15) null, d4 decimal(38, 0) null)");
// Complex types
AnalysisError("alter table functional_kudu.testtbl add columns ( "+
"a struct<f1:int>)", "Kudu tables do not support complex types: " +
"a STRUCT<f1:INT>");
// Add primary key
AnalysisError("alter table functional_kudu.testtbl add columns (a int primary key)",
"Cannot add a primary key using an ALTER TABLE ADD COLUMNS statement: " +
"a INT PRIMARY KEY");
// Columns requiring a default value
AnalyzesOk("alter table functional_kudu.testtbl add columns (a1 int not null " +
"default 10)");
AnalyzesOk("alter table functional_kudu.testtbl add columns (a1 int null " +
"default 10)");
AnalyzesOk("alter table functional_kudu.testtbl add columns (d1 decimal(9, 2) null " +
"default 99.99)");
// Other Kudu column options
AnalyzesOk("alter table functional_kudu.testtbl add columns (a int encoding rle)");
AnalyzesOk("alter table functional_kudu.testtbl add columns (a int compression lz4)");
AnalyzesOk("alter table functional_kudu.testtbl add columns (a int block_size 10)");
// REPLACE columns is not supported for Kudu tables
AnalysisError("alter table functional_kudu.testtbl replace columns (a int null)",
"ALTER TABLE REPLACE COLUMNS is not supported on Kudu tables");
// Conflict with existing column
AnalysisError("alter table functional_kudu.testtbl add columns (zip int)",
"Column already exists: zip");
// Kudu column options on an HDFS table
AnalysisError("alter table functional.alltypes add columns (a int not null)",
"The specified column options are only supported in Kudu tables: " +
"a INT NOT NULL");
// ALTER TABLE DROP COLUMN
AnalyzesOk("alter table functional_kudu.testtbl drop column name");
AnalysisError("alter table functional_kudu.testtbl drop column no_col",
"Column 'no_col' does not exist in table: functional_kudu.testtbl");
// ALTER TABLE CHANGE COLUMN on Kudu tables
AnalyzesOk("alter table functional_kudu.testtbl change column name new_name string");
AnalyzesOk("alter table functional_kudu.testtbl change column zip " +
"zip int comment 'comment'");
// Unsupported column options
AnalysisError("alter table functional_kudu.testtbl change column zip zip_code int " +
"encoding rle compression lz4 default 90000", "Unsupported column options in " +
"ALTER TABLE CHANGE COLUMN statement: 'zip_code INT ENCODING RLE COMPRESSION " +
"LZ4 DEFAULT 90000'. Use ALTER TABLE ALTER COLUMN instead.");
// Changing the column type is not supported for Kudu tables
AnalysisError("alter table functional_kudu.testtbl change column zip zip bigint",
"Cannot change the type of a Kudu column using an ALTER TABLE CHANGE COLUMN " +
"statement: (INT vs BIGINT)");
// Setting kudu.table_id is not allowed for Kudu tables.
AnalysisError("ALTER TABLE functional_kudu.testtbl SET " +
"TBLPROPERTIES ('kudu.table_id' = '1234')",
"Property 'kudu.table_id' cannot be altered for Kudu tables");
// Setting 'external.table.purge' is not allowed for Kudu tables.
AnalysisError("ALTER TABLE functional_kudu.testtbl SET " +
"TBLPROPERTIES ('external.table.purge' = 'true')",
"Property 'external.table.purge' cannot be altered for Kudu tables");
// Rename the underlying Kudu table is not supported for managed Kudu tables.
AnalysisError("ALTER TABLE functional_kudu.testtbl SET " +
"TBLPROPERTIES ('kudu.table_name' = 'Hans')",
"Not allowed to set 'kudu.table_name' manually for managed Kudu tables");
// TODO IMPALA-6375: Allow setting kudu.table_name for managed Kudu tables
// if the 'EXTERNAL' property is set to TRUE in the same step.
AnalysisError("ALTER TABLE functional_kudu.testtbl SET " +
"TBLPROPERTIES ('EXTERNAL' = 'TRUE','kudu.table_name' = 'Hans')",
"Not allowed to set 'kudu.table_name' manually for managed Kudu tables");
// ALTER TABLE RENAME TO
AnalyzesOk("ALTER TABLE functional_kudu.testtbl RENAME TO new_testtbl");
// ALTER TABLE SORT BY
AnalysisError("alter table functional_kudu.alltypes sort by (int_col)",
"ALTER TABLE SORT BY not supported on Kudu tables.");
BackendConfig.INSTANCE.setZOrderSortUnlocked(true);
// ALTER TABLE SORT BY ZORDER
AnalysisError("alter table functional_kudu.alltypes sort by zorder (int_col)",
"ALTER TABLE SORT BY not supported on Kudu tables.");
// ALTER TABLE SET TBLPROPERTIES for sort.columns
AnalysisError("alter table functional_kudu.alltypes set tblproperties(" +
"'sort.columns'='int_col')",
"'sort.*' table properties are not supported for Kudu tables.");
// ALTER TABLE SET TBLPROPERTIES for sort.order
AnalysisError("alter table functional_kudu.alltypes set tblproperties("
+ "'sort.order'='true')",
"'sort.*' table properties are not supported for Kudu tables.");
BackendConfig.INSTANCE.setZOrderSortUnlocked(false);
}
}