diff --git a/plugin/trino-accumulo/src/main/java/io/trino/plugin/accumulo/AccumuloMetadata.java b/plugin/trino-accumulo/src/main/java/io/trino/plugin/accumulo/AccumuloMetadata.java index f6730a0b7a80..4756126540cc 100644 --- a/plugin/trino-accumulo/src/main/java/io/trino/plugin/accumulo/AccumuloMetadata.java +++ b/plugin/trino-accumulo/src/main/java/io/trino/plugin/accumulo/AccumuloMetadata.java @@ -309,13 +309,15 @@ public List listTables(ConnectorSession session, Optional schemaNames = filterSchema.>map(ImmutableSet::of) .orElseGet(client::getSchemaNames); - ImmutableList.Builder builder = ImmutableList.builder(); + ImmutableSet.Builder builder = ImmutableSet.builder(); for (String schemaName : schemaNames) { for (String tableName : client.getTableNames(schemaName)) { builder.add(new SchemaTableName(schemaName, tableName)); } } - return builder.build(); + builder.addAll(listViews(session, filterSchema)); + // Deduplicate with set because state may change concurrently + return builder.build().asList(); } @Override diff --git a/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java b/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java index 7e51903b5c4d..81f2a6fce262 100644 --- a/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java +++ b/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java @@ -135,10 +135,14 @@ public ConnectorTableMetadata getTableMetadata(ConnectorSession session, Connect @Override public List listTables(ConnectorSession session, Optional schemaName) { - return tables.values().stream() - .filter(table -> schemaName.isEmpty() || table.getSchemaName().equals(schemaName.get())) - .map(BlackHoleTableHandle::toSchemaTableName) - .collect(toList()); + // Deduplicate with set because state may change concurrently + return ImmutableSet.builder() + .addAll(tables.values().stream() + .filter(table -> schemaName.isEmpty() || table.getSchemaName().equals(schemaName.get())) + .map(BlackHoleTableHandle::toSchemaTableName) + .collect(toList())) + .addAll(listViews(session, schemaName)) + .build().asList(); } @Override diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoHiveCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoHiveCatalog.java index 6c99b6a436dd..f8a1916e83b3 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoHiveCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoHiveCatalog.java @@ -15,6 +15,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import io.airlift.log.Logger; import io.trino.plugin.base.CatalogName; import io.trino.plugin.hive.HdfsEnvironment; @@ -277,7 +278,7 @@ public Transaction newCreateTableTransaction( @Override public List listTables(ConnectorSession session, Optional namespace) { - ImmutableList.Builder tablesListBuilder = ImmutableList.builder(); + ImmutableSet.Builder tablesListBuilder = ImmutableSet.builder(); listNamespaces(session, namespace) .stream() .flatMap(schema -> Stream.concat( @@ -291,8 +292,10 @@ public List listTables(ConnectorSession session, Optional listViews(ConnectorSession session, Optional namespace) { - // Filter on PRESTO_VIEW_COMMENT to distinguish from materialized views return listNamespaces(session, namespace).stream() - .flatMap(schema -> - metastore.getTablesWithParameter(schema, TABLE_COMMENT, PRESTO_VIEW_COMMENT).stream() - .map(table -> new SchemaTableName(schema, table))) + .flatMap(this::listViews) .collect(toImmutableList()); } + private Stream listViews(String schema) + { + // Filter on PRESTO_VIEW_COMMENT to distinguish from materialized views + return metastore.getTablesWithParameter(schema, TABLE_COMMENT, PRESTO_VIEW_COMMENT).stream() + .map(table -> new SchemaTableName(schema, table)); + } + @Override public Map getViews(ConnectorSession session, Optional namespace) { diff --git a/plugin/trino-raptor-legacy/src/main/java/io/trino/plugin/raptor/legacy/RaptorMetadata.java b/plugin/trino-raptor-legacy/src/main/java/io/trino/plugin/raptor/legacy/RaptorMetadata.java index bcaf3eed209d..ab851357fdb5 100644 --- a/plugin/trino-raptor-legacy/src/main/java/io/trino/plugin/raptor/legacy/RaptorMetadata.java +++ b/plugin/trino-raptor-legacy/src/main/java/io/trino/plugin/raptor/legacy/RaptorMetadata.java @@ -249,7 +249,11 @@ public ConnectorTableMetadata getTableMetadata(ConnectorSession session, Connect @Override public List listTables(ConnectorSession session, Optional schemaName) { - return dao.listTables(schemaName.orElse(null)); + // Deduplicate with set because state may change concurrently + return ImmutableSet.builder() + .addAll(dao.listTables(schemaName.orElse(null))) + .addAll(listViews(session, schemaName)) + .build().asList(); } @Override diff --git a/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestDistributedQueries.java b/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestDistributedQueries.java index e2a4a419aa18..d3f1e74c54f4 100644 --- a/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestDistributedQueries.java +++ b/testing/trino-testing/src/main/java/io/trino/testing/AbstractTestDistributedQueries.java @@ -795,7 +795,7 @@ public void testView() return; } - @Language("SQL") String query = "SELECT orderkey, orderstatus, totalprice / 2 half FROM orders"; + @Language("SQL") String query = "SELECT orderkey, orderstatus, (totalprice / 2) half FROM orders"; String catalogName = getSession().getCatalog().orElseThrow(); String schemaName = getSession().getSchema().orElseThrow(); @@ -807,6 +807,7 @@ public void testView() assertUpdate("CREATE VIEW " + testViewWithComment + " COMMENT 'orders' AS SELECT 123 x"); assertUpdate("CREATE OR REPLACE VIEW " + testViewWithComment + " COMMENT 'orders' AS " + query); + // verify comment MaterializedResult materializedRows = computeActual("SHOW CREATE VIEW " + testViewWithComment); assertThat((String) materializedRows.getOnlyValue()).contains("COMMENT 'orders'"); assertThat(query( @@ -816,6 +817,7 @@ public void testView() .skippingTypesCheck() .containsAll("VALUES ('" + testView + "', null), ('" + testViewWithComment + "', 'orders')"); + // reading assertQuery("SELECT * FROM " + testView, query); assertQuery("SELECT * FROM " + testViewWithComment, query); @@ -828,8 +830,114 @@ public void testView() String name = format("%s.%s." + testView, catalogName, schemaName); assertQuery("SELECT * FROM " + name, query); - assertUpdate("DROP VIEW " + testView); assertUpdate("DROP VIEW " + testViewWithComment); + + // information_schema.views without table_name filter + assertThat(query( + "SELECT table_name, regexp_replace(view_definition, '\\s', '') FROM information_schema.views " + + "WHERE table_schema = '" + schemaName + "'")) + .skippingTypesCheck() + .containsAll("VALUES ('" + testView + "', '" + query.replaceAll("\\s", "") + "')"); + // information_schema.views with table_name filter + assertQuery( + "SELECT table_name, regexp_replace(view_definition, '\\s', '') FROM information_schema.views " + + "WHERE table_schema = '" + schemaName + "' and table_name = '" + testView + "'", + "VALUES ('" + testView + "', '" + query.replaceAll("\\s", "") + "')"); + + // table listing + assertThat(query("SHOW TABLES")) + .skippingTypesCheck() + .containsAll("VALUES '" + testView + "'"); + // information_schema.tables without table_name filter + assertThat(query( + "SELECT table_name, table_type FROM information_schema.tables " + + "WHERE table_schema = '" + schemaName + "'")) + .skippingTypesCheck() + .containsAll("VALUES ('" + testView + "', 'VIEW')"); + // information_schema.tables with table_name filter + assertQuery( + "SELECT table_name, table_type FROM information_schema.tables " + + "WHERE table_schema = '" + schemaName + "' and table_name = '" + testView + "'", + "VALUES ('" + testView + "', 'VIEW')"); + + // system.jdbc.tables without filter + assertThat(query("SELECT table_schem, table_name, table_type FROM system.jdbc.tables")) + .skippingTypesCheck() + .containsAll("VALUES ('" + schemaName + "', '" + testView + "', 'VIEW')"); + + // system.jdbc.tables with table prefix filter + assertQuery( + "SELECT table_schem, table_name, table_type " + + "FROM system.jdbc.tables " + + "WHERE table_cat = '" + catalogName + "' AND " + + "table_schem = '" + schemaName + "' AND " + + "table_name = '" + testView + "'", + "VALUES ('" + schemaName + "', '" + testView + "', 'VIEW')"); + + // column listing + assertThat(query("SHOW COLUMNS FROM " + testView)) + .projected(0) // column types can very between connectors + .skippingTypesCheck() + .matches("VALUES 'orderkey', 'orderstatus', 'half'"); + + assertThat(query("DESCRIBE " + testView)) + .projected(0) // column types can very between connectors + .skippingTypesCheck() + .matches("VALUES 'orderkey', 'orderstatus', 'half'"); + + // information_schema.columns without table_name filter + assertThat(query( + "SELECT table_name, column_name " + + "FROM information_schema.columns " + + "WHERE table_schema = '" + schemaName + "'")) + .skippingTypesCheck() + .containsAll( + "SELECT * FROM (VALUES '" + testView + "') " + + "CROSS JOIN UNNEST(ARRAY['orderkey', 'orderstatus', 'half'])"); + + // information_schema.columns with table_name filter + assertThat(query( + "SELECT table_name, column_name " + + "FROM information_schema.columns " + + "WHERE table_schema = '" + schemaName + "' and table_name = '" + testView + "'")) + .skippingTypesCheck() + .containsAll( + "SELECT * FROM (VALUES '" + testView + "') " + + "CROSS JOIN UNNEST(ARRAY['orderkey', 'orderstatus', 'half'])"); + + // view-specific listings + assertThat(query("SELECT table_name FROM information_schema.views WHERE table_schema = '" + schemaName + "'")) + .skippingTypesCheck() + .containsAll("VALUES '" + testView + "'"); + + // system.jdbc.columns without filter + assertThat(query("SELECT table_schem, table_name, column_name FROM system.jdbc.columns")) + .skippingTypesCheck() + .containsAll( + "SELECT * FROM (VALUES ('" + schemaName + "', '" + testView + "')) " + + "CROSS JOIN UNNEST(ARRAY['orderkey', 'orderstatus', 'half'])"); + + // system.jdbc.columns with schema filter + assertThat(query( + "SELECT table_schem, table_name, column_name " + + "FROM system.jdbc.columns " + + "WHERE table_schem LIKE '%" + schemaName + "%'")) + .skippingTypesCheck() + .containsAll( + "SELECT * FROM (VALUES ('" + schemaName + "', '" + testView + "')) " + + "CROSS JOIN UNNEST(ARRAY['orderkey', 'orderstatus', 'half'])"); + + // system.jdbc.columns with table filter + assertThat(query( + "SELECT table_schem, table_name, column_name " + + "FROM system.jdbc.columns " + + "WHERE table_name LIKE '%" + testView + "%'")) + .skippingTypesCheck() + .containsAll( + "SELECT * FROM (VALUES ('" + schemaName + "', '" + testView + "')) " + + "CROSS JOIN UNNEST(ARRAY['orderkey', 'orderstatus', 'half'])"); + + assertUpdate("DROP VIEW " + testView); } @Test diff --git a/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java b/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java index c7ac5b5cf7f7..45ed82a49200 100644 --- a/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java +++ b/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java @@ -624,14 +624,11 @@ public void testMaterializedView() checkInformationSchemaViewsForMaterializedView(view.getSchemaName(), view.getObjectName()); // system.jdbc.columns without filter - @Language("SQL") String expectedValues = "VALUES ('" + view.getSchemaName() + "', '" + view.getObjectName() + "', 'nationkey'), " + - "('" + view.getSchemaName() + "', '" + view.getObjectName() + "', 'name'), " + - "('" + view.getSchemaName() + "', '" + view.getObjectName() + "', 'regionkey'), " + - "('" + view.getSchemaName() + "', '" + view.getObjectName() + "', 'comment')"; - assertThat(query( - "SELECT table_schem, table_name, column_name FROM system.jdbc.columns")) + assertThat(query("SELECT table_schem, table_name, column_name FROM system.jdbc.columns")) .skippingTypesCheck() - .containsAll(expectedValues); + .containsAll( + "SELECT * FROM (VALUES ('" + view.getSchemaName() + "', '" + view.getObjectName() + "')) " + + "CROSS JOIN UNNEST(ARRAY['nationkey', 'name', 'regionkey', 'comment'])"); // system.jdbc.columns with schema filter assertThat(query( @@ -639,14 +636,19 @@ public void testMaterializedView() "FROM system.jdbc.columns " + "WHERE table_schem LIKE '%" + view.getSchemaName() + "%'")) .skippingTypesCheck() - .containsAll(expectedValues); + .containsAll( + "SELECT * FROM (VALUES ('" + view.getSchemaName() + "', '" + view.getObjectName() + "')) " + + "CROSS JOIN UNNEST(ARRAY['nationkey', 'name', 'regionkey', 'comment'])"); // system.jdbc.columns with table filter - assertQuery( + assertThat(query( "SELECT table_schem, table_name, column_name " + "FROM system.jdbc.columns " + - "WHERE table_name LIKE '%" + view.getObjectName() + "%'", - expectedValues); + "WHERE table_name LIKE '%" + view.getObjectName() + "%'")) + .skippingTypesCheck() + .containsAll( + "SELECT * FROM (VALUES ('" + view.getSchemaName() + "', '" + view.getObjectName() + "')) " + + "CROSS JOIN UNNEST(ARRAY['nationkey', 'name', 'regionkey', 'comment'])"); // details assertThat(((String) computeScalar("SHOW CREATE MATERIALIZED VIEW " + view.getObjectName())))