diff --git a/plugin/trino-kudu/pom.xml b/plugin/trino-kudu/pom.xml
index f071d5476324..b49f81a77aa9 100644
--- a/plugin/trino-kudu/pom.xml
+++ b/plugin/trino-kudu/pom.xml
@@ -43,6 +43,12 @@
log
+
+ io.airlift
+ log-manager
+ compile
+
+
io.airlift
units
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduConnectorTest.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduConnectorTest.java
new file mode 100644
index 000000000000..2f1715227db1
--- /dev/null
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduConnectorTest.java
@@ -0,0 +1,440 @@
+/*
+ * Licensed 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 io.trino.plugin.kudu;
+
+import io.trino.sql.planner.plan.LimitNode;
+import io.trino.testing.BaseConnectorTest;
+import io.trino.testing.MaterializedResult;
+import io.trino.testing.QueryRunner;
+import io.trino.testing.TestingConnectorBehavior;
+import io.trino.testing.sql.TestTable;
+import org.testng.SkipException;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import static io.trino.plugin.kudu.KuduQueryRunnerFactory.createKuduQueryRunnerTpch;
+import static io.trino.spi.type.VarcharType.VARCHAR;
+import static io.trino.testing.MaterializedResult.resultBuilder;
+import static io.trino.testing.assertions.Assert.assertEquals;
+import static io.trino.testing.sql.TestTable.randomTableSuffix;
+import static java.lang.String.format;
+import static java.util.Locale.ENGLISH;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.testng.Assert.assertTrue;
+
+public abstract class AbstractKuduConnectorTest
+ extends BaseConnectorTest
+{
+ private TestingKuduServer kuduServer;
+
+ protected abstract String getKuduServerVersion();
+
+ protected abstract Optional getKuduSchemaEmulationPrefix();
+
+ @Override
+ protected QueryRunner createQueryRunner()
+ throws Exception
+ {
+ kuduServer = new TestingKuduServer(getKuduServerVersion());
+ return createKuduQueryRunnerTpch(kuduServer, getKuduSchemaEmulationPrefix(), REQUIRED_TPCH_TABLES);
+ }
+
+ @AfterClass(alwaysRun = true)
+ public final void destroy()
+ {
+ kuduServer.close();
+ }
+
+ @Override
+ protected boolean hasBehavior(TestingConnectorBehavior connectorBehavior)
+ {
+ switch (connectorBehavior) {
+ case SUPPORTS_DELETE:
+ return true;
+ case SUPPORTS_RENAME_SCHEMA:
+ case SUPPORTS_COMMENT_ON_TABLE:
+ case SUPPORTS_COMMENT_ON_COLUMN:
+ case SUPPORTS_ARRAY:
+ case SUPPORTS_NOT_NULL_CONSTRAINT:
+ case SUPPORTS_TOPN_PUSHDOWN:
+ return false;
+ default:
+ return super.hasBehavior(connectorBehavior);
+ }
+ }
+
+ @Test
+ @Override
+ public void testDescribeTable()
+ {
+ String extra = "nullable, encoding=auto, compression=default";
+ MaterializedResult expectedColumns = resultBuilder(getSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR)
+ .row("orderkey", "bigint", extra, "")
+ .row("custkey", "bigint", extra, "")
+ .row("orderstatus", "varchar", extra, "")
+ .row("totalprice", "double", extra, "")
+ .row("orderdate", "varchar", extra, "")
+ .row("orderpriority", "varchar", extra, "")
+ .row("clerk", "varchar", extra, "")
+ .row("shippriority", "integer", extra, "")
+ .row("comment", "varchar", extra, "")
+ .build();
+ MaterializedResult actualColumns = computeActual("DESCRIBE orders");
+ assertEquals(actualColumns, expectedColumns);
+ }
+
+ @Test
+ @Override
+ public void testShowColumns()
+ {
+ MaterializedResult actual = computeActual("SHOW COLUMNS FROM orders");
+
+ MaterializedResult expectedParametrizedVarchar = resultBuilder(getSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR)
+ .row("orderkey", "bigint", "nullable, encoding=auto, compression=default", "")
+ .row("custkey", "bigint", "nullable, encoding=auto, compression=default", "")
+ .row("orderstatus", "varchar", "nullable, encoding=auto, compression=default", "")
+ .row("totalprice", "double", "nullable, encoding=auto, compression=default", "")
+ .row("orderdate", "varchar", "nullable, encoding=auto, compression=default", "")
+ .row("orderpriority", "varchar", "nullable, encoding=auto, compression=default", "")
+ .row("clerk", "varchar", "nullable, encoding=auto, compression=default", "")
+ .row("shippriority", "integer", "nullable, encoding=auto, compression=default", "")
+ .row("comment", "varchar", "nullable, encoding=auto, compression=default", "")
+ .build();
+
+ assertEquals(actual, expectedParametrizedVarchar);
+ }
+
+ @Test
+ @Override
+ public void testShowCreateTable()
+ {
+ assertThat((String) computeActual("SHOW CREATE TABLE orders").getOnlyValue())
+ .matches("CREATE TABLE kudu\\.\\w+\\.orders \\Q(\n" +
+ " orderkey bigint WITH ( nullable = true ),\n" +
+ " custkey bigint WITH ( nullable = true ),\n" +
+ " orderstatus varchar WITH ( nullable = true ),\n" +
+ " totalprice double WITH ( nullable = true ),\n" +
+ " orderdate varchar WITH ( nullable = true ),\n" +
+ " orderpriority varchar WITH ( nullable = true ),\n" +
+ " clerk varchar WITH ( nullable = true ),\n" +
+ " shippriority integer WITH ( nullable = true ),\n" +
+ " comment varchar WITH ( nullable = true )\n" +
+ ")\n" +
+ "WITH (\n" +
+ " number_of_replicas = 3,\n" +
+ " partition_by_hash_buckets = 2,\n" +
+ " partition_by_hash_columns = ARRAY['row_uuid'],\n" +
+ " partition_by_range_columns = ARRAY['row_uuid'],\n" +
+ " range_partitions = '[{\"lower\":null,\"upper\":null}]'\n" +
+ ")");
+
+ assertUpdate("CREATE TABLE IF NOT EXISTS test_show_create_table (\n" +
+ "id INT WITH (primary_key=true),\n" +
+ "user_name VARCHAR\n" +
+ ") WITH (\n" +
+ " partition_by_hash_columns = ARRAY['id'],\n" +
+ " partition_by_hash_buckets = 2," +
+ " number_of_replicas = 1\n" +
+ ")");
+
+ MaterializedResult result = computeActual("SHOW CREATE TABLE test_show_create_table");
+ String sqlStatement = (String) result.getOnlyValue();
+ String tableProperties = sqlStatement.split("\\)\\s*WITH\\s*\\(")[1];
+ assertTableProperty(tableProperties, "number_of_replicas", "1");
+ assertTableProperty(tableProperties, "partition_by_hash_columns", Pattern.quote("ARRAY['id']"));
+ assertTableProperty(tableProperties, "partition_by_hash_buckets", "2");
+
+ assertUpdate("DROP TABLE test_show_create_table");
+ }
+
+ @Test
+ public void testRowDelete()
+ {
+ assertUpdate("CREATE TABLE IF NOT EXISTS test_row_delete (" +
+ "id INT WITH (primary_key=true), " +
+ "second_id INT, " +
+ "user_name VARCHAR" +
+ ") WITH (" +
+ " partition_by_hash_columns = ARRAY['id'], " +
+ " partition_by_hash_buckets = 2" +
+ ")");
+
+ assertUpdate("INSERT INTO test_row_delete VALUES (0, 1, 'user0'), (3, 4, 'user2'), (2, 3, 'user2'), (1, 2, 'user1')", 4);
+ assertQuery("SELECT count(*) FROM test_row_delete", "VALUES 4");
+
+ assertUpdate("DELETE FROM test_row_delete WHERE second_id = 4", 1);
+ assertQuery("SELECT count(*) FROM test_row_delete", "VALUES 3");
+
+ assertUpdate("DELETE FROM test_row_delete WHERE user_name = 'user1'", 1);
+ assertQuery("SELECT count(*) FROM test_row_delete", "VALUES 2");
+
+ assertUpdate("DELETE FROM test_row_delete WHERE id = 0", 1);
+ assertQuery("SELECT * FROM test_row_delete", "VALUES (2, 3, 'user2')");
+
+ assertUpdate("DROP TABLE test_row_delete");
+ }
+
+ @Test(dataProvider = "testColumnNameDataProvider")
+ @Override
+ public void testColumnName(String columnName)
+ {
+ if (!requiresDelimiting(columnName)) {
+ testColumnName(columnName, false);
+ }
+ testColumnName(columnName, true);
+ }
+
+ private void testColumnName(String columnName, boolean delimited)
+ {
+ String nameInSql = columnName;
+ if (delimited) {
+ nameInSql = "\"" + columnName.replace("\"", "\"\"") + "\"";
+ }
+ String tableName = "tcn_" + nameInSql.toLowerCase(ENGLISH).replaceAll("[^a-z0-9]", "") + randomTableSuffix();
+
+ try {
+ // TODO test with both CTAS *and* CREATE TABLE + INSERT, since they use different connector API methods.
+ assertUpdate("" +
+ "CREATE TABLE " + tableName + "(key varchar(50) WITH (primary_key=true), " + nameInSql + " varchar(50) WITH (nullable=true)) " +
+ "WITH (partition_by_hash_columns = ARRAY['key'], partition_by_hash_buckets = 3)");
+ }
+ catch (RuntimeException e) {
+ if (isColumnNameRejected(e, columnName, delimited)) {
+ // It is OK if give column name is not allowed and is clearly rejected by the connector.
+ return;
+ }
+ throw e;
+ }
+ try {
+ assertUpdate("INSERT INTO " + tableName + " VALUES ('null value', NULL), ('sample value', 'abc'), ('other value', 'xyz')", 3);
+
+ // SELECT *
+ assertQuery("SELECT * FROM " + tableName, "VALUES ('null value', NULL), ('sample value', 'abc'), ('other value', 'xyz')");
+
+ // projection
+ assertQuery("SELECT " + nameInSql + " FROM " + tableName, "VALUES (NULL), ('abc'), ('xyz')");
+
+ // predicate
+ assertQuery("SELECT key FROM " + tableName + " WHERE " + nameInSql + " IS NULL", "VALUES ('null value')");
+ assertQuery("SELECT key FROM " + tableName + " WHERE " + nameInSql + " = 'abc'", "VALUES ('sample value')");
+ }
+ finally {
+ assertUpdate("DROP TABLE " + tableName);
+ }
+ }
+
+ @Test
+ public void testProjection()
+ {
+ assertUpdate("CREATE TABLE IF NOT EXISTS test_projection (" +
+ "id INT WITH (primary_key=true), " +
+ "user_name VARCHAR " +
+ ") WITH (" +
+ " partition_by_hash_columns = ARRAY['id'], " +
+ " partition_by_hash_buckets = 2" +
+ ")");
+
+ assertUpdate("INSERT INTO test_projection VALUES (0, 'user0'), (2, 'user2'), (1, 'user1')", 3);
+
+ assertQuery("SELECT id, 'test' FROM test_projection ORDER BY id", "VALUES (0, 'test'), (1, 'test'), (2, 'test')");
+
+ assertUpdate("DROP TABLE test_projection");
+ }
+
+ @Test
+ @Override
+ public void testExplainAnalyzeWithDeleteWithSubquery()
+ {
+ String tableName = "test_delete_" + randomTableSuffix();
+
+ // delete using a subquery
+ assertUpdate("CREATE TABLE " + tableName + " AS SELECT * FROM nation", 25);
+ assertExplainAnalyze("EXPLAIN ANALYZE DELETE FROM " + tableName + " WHERE regionkey IN (SELECT regionkey FROM region WHERE name LIKE 'A%' LIMIT 1)",
+ "SemiJoin.*");
+ assertUpdate("DROP TABLE " + tableName);
+ }
+
+ @Test
+ @Override
+ public void testCreateTable()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testAddColumn()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testInsert()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testInsertUnicode()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testInsertHighestUnicodeCharacter()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Override
+ public void testInsertNegativeDate()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testDelete()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testDeleteWithSemiJoin()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testDeleteWithVarcharPredicate()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testDeleteWithComplexPredicate()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testDeleteWithSubquery()
+ {
+ // TODO Support these test once kudu connector can create tables with default partitions
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testWrittenStats()
+ {
+ // TODO Kudu connector supports CTAS and inserts, but the test would fail
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ @Override
+ public void testCreateTableAsSelectNegativeDate()
+ {
+ // Map date column type to varchar
+ String tableName = "negative_date_" + randomTableSuffix();
+
+ try {
+ assertUpdate(format("CREATE TABLE %s AS SELECT DATE '-0001-01-01' AS dt", tableName), 1);
+ assertQuery("SELECT * FROM " + tableName, "VALUES '-0001-01-01'");
+ assertQuery(format("SELECT * FROM %s WHERE dt = '-0001-01-01'", tableName), "VALUES '-0001-01-01'");
+ }
+ finally {
+ assertUpdate("DROP TABLE IF EXISTS " + tableName);
+ }
+ }
+
+ @Test
+ @Override
+ public void testDateYearOfEraPredicate()
+ {
+ assertThatThrownBy(super::testDateYearOfEraPredicate)
+ .hasStackTraceContaining("Cannot apply operator: varchar = date");
+ }
+
+ @Test
+ @Override
+ public void testCharVarcharComparison()
+ {
+ assertThatThrownBy(super::testCharVarcharComparison)
+ .hasMessageContaining("For query: ")
+ .hasMessageContaining("Actual rows")
+ .hasMessageContaining("Expected rows");
+
+ throw new SkipException("TODO");
+ }
+
+ @Test
+ public void testLimitPushdown()
+ {
+ assertThat(query("SELECT name FROM nation LIMIT 30")).isNotFullyPushedDown(LimitNode.class); // Use high limit for result determinism
+ }
+
+ @Override
+ protected Optional filterDataMappingSmokeTestData(DataMappingTestSetup dataMappingTestSetup)
+ {
+ String typeName = dataMappingTestSetup.getTrinoTypeName();
+ if (typeName.equals("time")
+ || typeName.equals("timestamp(3) with time zone")) {
+ return Optional.of(dataMappingTestSetup.asUnsupported());
+ }
+
+ if (typeName.equals("date") // date gets stored as varchar
+ || typeName.equals("varbinary") // TODO (https://github.com/trinodb/trino/issues/3416)
+ || (typeName.startsWith("char") && dataMappingTestSetup.getSampleValueLiteral().contains(" "))) { // TODO: https://github.com/trinodb/trino/issues/3597
+ // TODO this should either work or fail cleanly
+ return Optional.empty();
+ }
+
+ return Optional.of(dataMappingTestSetup);
+ }
+
+ @Override
+ protected TestTable createTableWithDefaultColumns()
+ {
+ throw new SkipException("Kudu connector does not support column default values");
+ }
+
+ private void assertTableProperty(String tableProperties, String key, String regexValue)
+ {
+ assertTrue(Pattern.compile(key + "\\s*=\\s*" + regexValue + ",?\\s+").matcher(tableProperties).find(),
+ "Not found: " + key + " = " + regexValue + " in " + tableProperties);
+ }
+}
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduIntegrationSmokeTest.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduIntegrationSmokeTest.java
deleted file mode 100644
index a11fccd0051c..000000000000
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduIntegrationSmokeTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Licensed 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 io.trino.plugin.kudu;
-
-import io.trino.sql.planner.plan.LimitNode;
-import io.trino.testing.AbstractTestIntegrationSmokeTest;
-import io.trino.testing.MaterializedResult;
-import io.trino.testing.QueryRunner;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.Test;
-
-import java.util.Optional;
-import java.util.regex.Pattern;
-
-import static io.trino.plugin.kudu.KuduQueryRunnerFactory.createKuduQueryRunnerTpch;
-import static io.trino.spi.type.VarcharType.VARCHAR;
-import static io.trino.testing.MaterializedResult.resultBuilder;
-import static io.trino.testing.assertions.Assert.assertEquals;
-import static io.trino.tpch.TpchTable.CUSTOMER;
-import static io.trino.tpch.TpchTable.NATION;
-import static io.trino.tpch.TpchTable.ORDERS;
-import static io.trino.tpch.TpchTable.REGION;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.testng.Assert.assertTrue;
-
-public abstract class AbstractKuduIntegrationSmokeTest
- // TODO extend BaseConnectorTest
- extends AbstractTestIntegrationSmokeTest
-{
- private TestingKuduServer kuduServer;
-
- protected abstract String getKuduServerVersion();
-
- protected abstract Optional getKuduSchemaEmulationPrefix();
-
- @Override
- protected QueryRunner createQueryRunner()
- throws Exception
- {
- kuduServer = new TestingKuduServer(getKuduServerVersion());
- return createKuduQueryRunnerTpch(kuduServer, getKuduSchemaEmulationPrefix(), CUSTOMER, NATION, ORDERS, REGION);
- }
-
- @AfterClass(alwaysRun = true)
- public final void destroy()
- {
- kuduServer.close();
- }
-
- @Test
- @Override
- public void testDescribeTable()
- {
- String extra = "nullable, encoding=auto, compression=default";
- MaterializedResult expectedColumns = resultBuilder(getSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR)
- .row("orderkey", "bigint", extra, "")
- .row("custkey", "bigint", extra, "")
- .row("orderstatus", "varchar", extra, "")
- .row("totalprice", "double", extra, "")
- .row("orderdate", "varchar", extra, "")
- .row("orderpriority", "varchar", extra, "")
- .row("clerk", "varchar", extra, "")
- .row("shippriority", "integer", extra, "")
- .row("comment", "varchar", extra, "")
- .build();
- MaterializedResult actualColumns = computeActual("DESCRIBE orders");
- assertEquals(actualColumns, expectedColumns);
- }
-
- @Test
- @Override
- public void testShowCreateTable()
- {
- assertThat((String) computeActual("SHOW CREATE TABLE orders").getOnlyValue())
- .matches("CREATE TABLE kudu\\.\\w+\\.orders \\Q(\n" +
- " orderkey bigint WITH ( nullable = true ),\n" +
- " custkey bigint WITH ( nullable = true ),\n" +
- " orderstatus varchar WITH ( nullable = true ),\n" +
- " totalprice double WITH ( nullable = true ),\n" +
- " orderdate varchar WITH ( nullable = true ),\n" +
- " orderpriority varchar WITH ( nullable = true ),\n" +
- " clerk varchar WITH ( nullable = true ),\n" +
- " shippriority integer WITH ( nullable = true ),\n" +
- " comment varchar WITH ( nullable = true )\n" +
- ")\n" +
- "WITH (\n" +
- " number_of_replicas = 3,\n" +
- " partition_by_hash_buckets = 2,\n" +
- " partition_by_hash_columns = ARRAY['row_uuid'],\n" +
- " partition_by_range_columns = ARRAY['row_uuid'],\n" +
- " range_partitions = '[{\"lower\":null,\"upper\":null}]'\n" +
- ")");
-
- assertUpdate("CREATE TABLE IF NOT EXISTS test_show_create_table (\n" +
- "id INT WITH (primary_key=true),\n" +
- "user_name VARCHAR\n" +
- ") WITH (\n" +
- " partition_by_hash_columns = ARRAY['id'],\n" +
- " partition_by_hash_buckets = 2," +
- " number_of_replicas = 1\n" +
- ")");
-
- MaterializedResult result = computeActual("SHOW CREATE TABLE test_show_create_table");
- String sqlStatement = (String) result.getOnlyValue();
- String tableProperties = sqlStatement.split("\\)\\s*WITH\\s*\\(")[1];
- assertTableProperty(tableProperties, "number_of_replicas", "1");
- assertTableProperty(tableProperties, "partition_by_hash_columns", Pattern.quote("ARRAY['id']"));
- assertTableProperty(tableProperties, "partition_by_hash_buckets", "2");
-
- assertUpdate("DROP TABLE test_show_create_table");
- }
-
- @Test
- public void testRowDelete()
- {
- assertUpdate("CREATE TABLE IF NOT EXISTS test_row_delete (" +
- "id INT WITH (primary_key=true), " +
- "second_id INT, " +
- "user_name VARCHAR" +
- ") WITH (" +
- " partition_by_hash_columns = ARRAY['id'], " +
- " partition_by_hash_buckets = 2" +
- ")");
-
- assertUpdate("INSERT INTO test_row_delete VALUES (0, 1, 'user0'), (3, 4, 'user2'), (2, 3, 'user2'), (1, 2, 'user1')", 4);
- assertQuery("SELECT count(*) FROM test_row_delete", "VALUES 4");
-
- assertUpdate("DELETE FROM test_row_delete WHERE second_id = 4", 1);
- assertQuery("SELECT count(*) FROM test_row_delete", "VALUES 3");
-
- assertUpdate("DELETE FROM test_row_delete WHERE user_name = 'user1'", 1);
- assertQuery("SELECT count(*) FROM test_row_delete", "VALUES 2");
-
- assertUpdate("DELETE FROM test_row_delete WHERE id = 0", 1);
- assertQuery("SELECT * FROM test_row_delete", "VALUES (2, 3, 'user2')");
-
- assertUpdate("DROP TABLE test_row_delete");
- }
-
- @Test
- public void testProjection()
- {
- assertUpdate("CREATE TABLE IF NOT EXISTS test_projection (" +
- "id INT WITH (primary_key=true), " +
- "user_name VARCHAR " +
- ") WITH (" +
- " partition_by_hash_columns = ARRAY['id'], " +
- " partition_by_hash_buckets = 2" +
- ")");
-
- assertUpdate("INSERT INTO test_projection VALUES (0, 'user0'), (2, 'user2'), (1, 'user1')", 3);
-
- assertQuery("SELECT id, 'test' FROM test_projection ORDER BY id", "VALUES (0, 'test'), (1, 'test'), (2, 'test')");
-
- assertUpdate("DROP TABLE test_projection");
- }
-
- @Test
- public void testLimitPushdown()
- {
- assertThat(query("SELECT name FROM nation LIMIT 30")).isNotFullyPushedDown(LimitNode.class); // Use high limit for result determinism
- }
-
- private void assertTableProperty(String tableProperties, String key, String regexValue)
- {
- assertTrue(Pattern.compile(key + "\\s*=\\s*" + regexValue + ",?\\s+").matcher(tableProperties).find(),
- "Not found: " + key + " = " + regexValue + " in " + tableProperties);
- }
-}
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithEmptyInferSchema.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithEmptyInferSchema.java
deleted file mode 100644
index f281b00b281e..000000000000
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithEmptyInferSchema.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed 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 io.trino.plugin.kudu;
-
-import org.testng.annotations.Test;
-
-import java.util.Optional;
-
-public abstract class AbstractKuduSmokeTestWithEmptyInferSchema
- extends AbstractKuduIntegrationSmokeTest
-{
- @Override
- protected Optional getKuduSchemaEmulationPrefix()
- {
- return Optional.of("");
- }
-
- @Test
- public void testListingOfTableForDefaultSchema()
- {
- assertQuery("SHOW TABLES FROM default", "VALUES '$schemas'");
- }
-}
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithDisabledInferSchema.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithDisabledInferSchemaConnectorTest.java
similarity index 51%
rename from plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithDisabledInferSchema.java
rename to plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithDisabledInferSchemaConnectorTest.java
index fb26210c0d04..d1d927867da1 100644
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithDisabledInferSchema.java
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithDisabledInferSchemaConnectorTest.java
@@ -17,8 +17,10 @@
import java.util.Optional;
-public abstract class AbstractKuduSmokeTestWithDisabledInferSchema
- extends AbstractKuduIntegrationSmokeTest
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public abstract class AbstractKuduWithDisabledInferSchemaConnectorTest
+ extends AbstractKuduConnectorTest
{
@Override
protected Optional getKuduSchemaEmulationPrefix()
@@ -31,4 +33,28 @@ public void testListingOfTableForDefaultSchema()
{
assertQuery("SHOW TABLES FROM default", "VALUES ('customer'), ('nation'), ('orders'), ('region')");
}
+
+ @Test
+ @Override
+ public void testCreateSchema()
+ {
+ assertThatThrownBy(super::testCreateSchema)
+ .hasMessage("Creating schema in Kudu connector not allowed if schema emulation is disabled.");
+ }
+
+ @Test
+ @Override
+ public void testRenameTableAcrossSchema()
+ {
+ assertThatThrownBy(super::testRenameTableAcrossSchema)
+ .hasMessage("Creating schema in Kudu connector not allowed if schema emulation is disabled.");
+ }
+
+ @Test
+ @Override
+ public void testDropNonEmptySchema()
+ {
+ assertThatThrownBy(super::testDropNonEmptySchema)
+ .hasMessage("Creating schema in Kudu connector not allowed if schema emulation is disabled.");
+ }
}
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithEmptyInferSchemaConnectorTest.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithEmptyInferSchemaConnectorTest.java
new file mode 100644
index 000000000000..0bcf7c11759e
--- /dev/null
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithEmptyInferSchemaConnectorTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed 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 io.trino.plugin.kudu;
+
+import org.testng.annotations.Test;
+
+import java.util.Optional;
+
+import static io.trino.testing.sql.TestTable.randomTableSuffix;
+
+public abstract class AbstractKuduWithEmptyInferSchemaConnectorTest
+ extends AbstractKuduConnectorTest
+{
+ @Override
+ protected Optional getKuduSchemaEmulationPrefix()
+ {
+ return Optional.of("");
+ }
+
+ @Test
+ public void testListingOfTableForDefaultSchema()
+ {
+ assertQuery("SHOW TABLES FROM default", "VALUES '$schemas'");
+ }
+
+ @Test
+ @Override
+ public void testDropNonEmptySchema()
+ {
+ // Set column and table properties in CREATE TABLE statement
+ String schemaName = "test_drop_non_empty_schema_" + randomTableSuffix();
+
+ try {
+ assertUpdate("CREATE SCHEMA " + schemaName);
+ assertUpdate("CREATE TABLE " + schemaName + ".t(x int WITH (primary_key=true)) WITH (partition_by_hash_columns=ARRAY['x'], partition_by_hash_buckets=2)");
+ assertQueryFails("DROP SCHEMA " + schemaName, ".*Cannot drop non-empty schema '\\Q" + schemaName + "\\E'");
+ }
+ finally {
+ assertUpdate("DROP TABLE IF EXISTS " + schemaName + ".t");
+ assertUpdate("DROP SCHEMA IF EXISTS " + schemaName);
+ }
+ }
+}
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithStandardInferSchema.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithStandardInferSchemaConnectorTest.java
similarity index 89%
rename from plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithStandardInferSchema.java
rename to plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithStandardInferSchemaConnectorTest.java
index 0fe1502d3b13..b8f49b4fe811 100644
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduSmokeTestWithStandardInferSchema.java
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/AbstractKuduWithStandardInferSchemaConnectorTest.java
@@ -19,8 +19,8 @@
import static org.testng.Assert.assertEquals;
-public abstract class AbstractKuduSmokeTestWithStandardInferSchema
- extends AbstractKuduIntegrationSmokeTest
+public abstract class AbstractKuduWithStandardInferSchemaConnectorTest
+ extends AbstractKuduConnectorTest
{
@Override
protected Optional getKuduSchemaEmulationPrefix()
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduLatestSmokeTests.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduLatestConnectorTests.java
similarity index 74%
rename from plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduLatestSmokeTests.java
rename to plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduLatestConnectorTests.java
index fd587fdb0625..88d09e72e81d 100644
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduLatestSmokeTests.java
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduLatestConnectorTests.java
@@ -13,12 +13,12 @@
*/
package io.trino.plugin.kudu;
-public class KuduLatestSmokeTests
+public class KuduLatestConnectorTests
{
private static final String KUDU_VERSION = "1.15.0";
- public static class TestKuduSmokeTestWithDisabledInferSchema
- extends AbstractKuduSmokeTestWithDisabledInferSchema
+ public static class TestKuduWithDisabledInferSchemaConnectorTest
+ extends AbstractKuduWithDisabledInferSchemaConnectorTest
{
@Override
protected String getKuduServerVersion()
@@ -27,8 +27,8 @@ protected String getKuduServerVersion()
}
}
- public static class TestKuduSmokeTestWithEmptyInferSchema
- extends AbstractKuduSmokeTestWithEmptyInferSchema
+ public static class TestKuduWithEmptyInferSchemaConnectorTest
+ extends AbstractKuduWithEmptyInferSchemaConnectorTest
{
@Override
protected String getKuduServerVersion()
@@ -38,7 +38,7 @@ protected String getKuduServerVersion()
}
public static class TestKuduSmokeTestWithStandardInferSchema
- extends AbstractKuduSmokeTestWithStandardInferSchema
+ extends AbstractKuduWithStandardInferSchemaConnectorTest
{
@Override
protected String getKuduServerVersion()
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduQueryRunnerFactory.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduQueryRunnerFactory.java
index a63dcbdd4ad2..9494b78bca0c 100644
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduQueryRunnerFactory.java
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduQueryRunnerFactory.java
@@ -16,6 +16,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HostAndPort;
+import io.airlift.log.Logger;
+import io.airlift.log.Logging;
import io.trino.Session;
import io.trino.plugin.tpch.TpchPlugin;
import io.trino.testing.DistributedQueryRunner;
@@ -152,4 +154,19 @@ public static Session createSession(String schema)
.setSchema(schema)
.build();
}
+
+ public static void main(String[] args)
+ throws Exception
+ {
+ Logging.initialize();
+ DistributedQueryRunner queryRunner = (DistributedQueryRunner) createKuduQueryRunnerTpch(
+ new TestingKuduServer(),
+ Optional.empty(),
+ ImmutableMap.of(),
+ ImmutableMap.of("http-server.http.port", "8080"),
+ TpchTable.getTables());
+ Logger log = Logger.get(KuduQueryRunnerFactory.class);
+ log.info("======== SERVER STARTED ========");
+ log.info("\n====\n%s\n====", queryRunner.getCoordinator().getBaseUrl());
+ }
}
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduSmokeTests.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduSmokeTests.java
index c201b6593837..b7c92c325a7f 100644
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduSmokeTests.java
+++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/KuduSmokeTests.java
@@ -17,8 +17,8 @@ public class KuduSmokeTests
{
private static final String KUDU_VERSION = "1.13.0";
- public static class TestKuduSmokeTestWithDisabledInferSchema
- extends AbstractKuduSmokeTestWithDisabledInferSchema
+ public static class TestKuduWithDisabledInferSchemaConnectorTest
+ extends AbstractKuduWithDisabledInferSchemaConnectorTest
{
@Override
protected String getKuduServerVersion()
@@ -27,8 +27,8 @@ protected String getKuduServerVersion()
}
}
- public static class TestKuduSmokeTestWithEmptyInferSchema
- extends AbstractKuduSmokeTestWithEmptyInferSchema
+ public static class TestKuduWithEmptyInferSchemaConnectorTest
+ extends AbstractKuduWithEmptyInferSchemaConnectorTest
{
@Override
protected String getKuduServerVersion()
@@ -38,7 +38,7 @@ protected String getKuduServerVersion()
}
public static class TestKuduSmokeTestWithStandardInferSchema
- extends AbstractKuduSmokeTestWithStandardInferSchema
+ extends AbstractKuduWithStandardInferSchemaConnectorTest
{
@Override
protected String getKuduServerVersion()
diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduDistributedQueries.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduDistributedQueries.java
deleted file mode 100644
index 530b242f44c3..000000000000
--- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduDistributedQueries.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Licensed 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 io.trino.plugin.kudu;
-
-import io.trino.testing.AbstractTestDistributedQueries;
-import io.trino.testing.MaterializedResult;
-import io.trino.testing.QueryRunner;
-import io.trino.testing.sql.TestTable;
-import org.testng.SkipException;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.Test;
-
-import java.util.Optional;
-
-import static io.trino.plugin.kudu.KuduQueryRunnerFactory.createKuduQueryRunnerTpch;
-import static io.trino.spi.type.VarcharType.VARCHAR;
-import static io.trino.testing.MaterializedResult.resultBuilder;
-import static io.trino.testing.assertions.Assert.assertEquals;
-
-public class TestKuduDistributedQueries
- extends AbstractTestDistributedQueries
-{
- private TestingKuduServer kuduServer;
-
- @Override
- protected QueryRunner createQueryRunner()
- throws Exception
- {
- kuduServer = new TestingKuduServer();
- return createKuduQueryRunnerTpch(kuduServer, Optional.of(""), REQUIRED_TPCH_TABLES);
- }
-
- @AfterClass(alwaysRun = true)
- public void destroy()
- {
- kuduServer.close();
- }
-
- @Override
- protected boolean supportsViews()
- {
- return false;
- }
-
- @Override
- protected boolean supportsArrays()
- {
- return false;
- }
-
- @Override
- protected boolean supportsCommentOnTable()
- {
- return false;
- }
-
- @Override
- protected boolean supportsCommentOnColumn()
- {
- return false;
- }
-
- @Override
- protected TestTable createTableWithDefaultColumns()
- {
- throw new SkipException("Kudu connector does not support column default values");
- }
-
- @Override
- public void testInsert()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- }
-
- @Override
- public void testCommentTable()
- {
- // TODO
- throw new SkipException("TODO");
- }
-
- @Override
- public void testCommentColumn()
- {
- // TODO
- throw new SkipException("TODO");
- }
-
- @Override
- public void testAddColumn()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- throw new SkipException("TODO");
- }
-
- @Override
- public void testCreateTable()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- throw new SkipException("TODO");
- }
-
- @Override
- public void testInsertUnicode()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- throw new SkipException("TODO");
- }
-
- @Override
- public void testInsertHighestUnicodeCharacter()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- throw new SkipException("TODO");
- }
-
- @Override
- public void testDropNonEmptySchema()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- throw new SkipException("TODO");
- }
-
- @Override
- public void testDelete()
- {
- // TODO Support these test once kudu connector can create tables with default partitions
- throw new SkipException("TODO");
- }
-
- @Override
- protected void skipTestUnlessSupportsDeletes()
- {
- // TODO Remove override once kudu connector can create tables with default partitions
- if (!supportsDelete()) {
- assertQueryFails("DELETE FROM region", "This connector does not support deletes");
- throw new SkipException("This connector does not support deletes");
- }
- }
-
- @Override
- public void testShowColumns()
- {
- MaterializedResult actual = computeActual("SHOW COLUMNS FROM orders");
-
- MaterializedResult expectedParametrizedVarchar = resultBuilder(getSession(), VARCHAR, VARCHAR, VARCHAR, VARCHAR)
- .row("orderkey", "bigint", "nullable, encoding=auto, compression=default", "")
- .row("custkey", "bigint", "nullable, encoding=auto, compression=default", "")
- .row("orderstatus", "varchar", "nullable, encoding=auto, compression=default", "")
- .row("totalprice", "double", "nullable, encoding=auto, compression=default", "")
- .row("orderdate", "varchar", "nullable, encoding=auto, compression=default", "")
- .row("orderpriority", "varchar", "nullable, encoding=auto, compression=default", "")
- .row("clerk", "varchar", "nullable, encoding=auto, compression=default", "")
- .row("shippriority", "integer", "nullable, encoding=auto, compression=default", "")
- .row("comment", "varchar", "nullable, encoding=auto, compression=default", "")
- .build();
-
- assertEquals(actual, expectedParametrizedVarchar);
- }
-
- @Override
- @Test
- public void testWrittenStats()
- {
- // TODO Kudu connector supports CTAS and inserts, but the test would fail
- throw new SkipException("TODO");
- }
-
- @Override
- public void testColumnName(String columnName)
- {
- // TODO (https://github.com/trinodb/trino/issues/3477) enable the test
- throw new SkipException("TODO");
- }
-
- @Override
- protected Optional filterDataMappingSmokeTestData(DataMappingTestSetup dataMappingTestSetup)
- {
- String typeName = dataMappingTestSetup.getTrinoTypeName();
- if (typeName.equals("time")
- || typeName.equals("timestamp(3) with time zone")) {
- return Optional.of(dataMappingTestSetup.asUnsupported());
- }
-
- if (typeName.equals("date") // date gets stored as varchar
- || typeName.equals("varbinary") // TODO (https://github.com/trinodb/trino/issues/3416)
- || (typeName.startsWith("char") && dataMappingTestSetup.getSampleValueLiteral().contains(" "))) { // TODO: https://github.com/trinodb/trino/issues/3597
- // TODO this should either work or fail cleanly
- return Optional.empty();
- }
-
- return Optional.of(dataMappingTestSetup);
- }
-}