Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,24 @@ public void createMaterializedView(
}
}

@Override
public List<SchemaTableName> listMaterializedViews(ConnectorSession session, String schemaName)
{
ImmutableList.Builder<SchemaTableName> materializedViews = ImmutableList.builder();

List<SchemaTableName> views = listViews(session, Optional.of(schemaName));

for (SchemaTableName viewName : views) {
View icebergView = getIcebergView(session, viewName);
Map<String, String> properties = icebergView.properties();
if (properties.containsKey(PRESTO_MATERIALIZED_VIEW_FORMAT_VERSION)) {
materializedViews.add(viewName);
}
}

return materializedViews.build();
}

@Override
public Optional<MaterializedViewDefinition> getMaterializedView(ConnectorSession session, SchemaTableName viewName)
{
Expand Down Expand Up @@ -1599,8 +1617,7 @@ public void dropMaterializedView(ConnectorSession session, SchemaTableName viewN
@Override
public MaterializedViewStatus getMaterializedViewStatus(
ConnectorSession session,
SchemaTableName materializedViewName,
TupleDomain<String> baseQueryDomain)
SchemaTableName materializedViewName)
{
Optional<MaterializedViewDefinition> definition = getMaterializedView(session, materializedViewName);
if (definition.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,9 @@ public void testMaterializedViewMetadata()

assertUpdate("CREATE MATERIALIZED VIEW test_mv_metadata AS SELECT id, name FROM test_mv_metadata_base WHERE id > 0");

assertQueryReturnsEmptyResult("SELECT table_name FROM information_schema.tables " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_mv_metadata' AND table_type = 'MATERIALIZED VIEW'");
assertQuery("SELECT table_name, table_type FROM information_schema.tables " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_mv_metadata'",
"VALUES ('test_mv_metadata', 'MATERIALIZED VIEW')");

assertUpdate("DROP MATERIALIZED VIEW test_mv_metadata");
assertUpdate("DROP TABLE test_mv_metadata_base");
Expand Down Expand Up @@ -1400,4 +1401,123 @@ public void testCreateMaterializedViewWithSameNameAsExistingTable()
assertUpdate("DROP TABLE existing_table_name");
assertUpdate("DROP TABLE test_mv_base");
}

@Test
public void testInformationSchemaMaterializedViews()
{
assertUpdate("CREATE TABLE test_is_mv_base1 (id BIGINT, name VARCHAR, value BIGINT)");
assertUpdate("CREATE TABLE test_is_mv_base2 (category VARCHAR, amount BIGINT)");

assertUpdate("INSERT INTO test_is_mv_base1 VALUES (1, 'Alice', 100), (2, 'Bob', 200)", 2);
assertUpdate("INSERT INTO test_is_mv_base2 VALUES ('A', 50), ('B', 75)", 2);

assertUpdate("CREATE MATERIALIZED VIEW test_is_mv1 AS SELECT id, name, value FROM test_is_mv_base1 WHERE id > 0");
Comment on lines +1405 to +1414
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Consider asserting information_schema.materialized_views emptiness or absence after dropping materialized views

Both testInformationSchemaMaterializedViews and testInformationSchemaMaterializedViewsAfterRefresh drop the materialized views (and base tables) but never verify the post-drop state. Please add a final assertion that the corresponding rows are removed from information_schema.materialized_views (for example, SELECT COUNT(*) FROM information_schema.materialized_views WHERE table_schema = 'test_schema' AND table_name = '...' returns 0) to confirm the information schema is updated on DROP as well as on CREATE/REFRESH.

Suggested implementation:

        assertUpdate("DROP MATERIALIZED VIEW test_mv_metadata");
        assertUpdate("DROP TABLE test_mv_metadata_base");
        assertUpdate("DROP TABLE existing_table_name");
        assertUpdate("DROP TABLE test_mv_base");

        assertQuery(
                "SELECT COUNT(*) " +
                        "FROM information_schema.materialized_views " +
                        "WHERE table_schema = 'test_schema' AND table_name = 'test_mv_metadata'",
                "VALUES 0");

To fully implement your suggestion for the specific tests you mentioned, the following additional updates are needed elsewhere in the same file:

  1. In testInformationSchemaMaterializedViews:

    • After the existing DROP statements that remove test_is_mv1, test_is_mv2 and their base tables, add assertions verifying that the corresponding rows are removed from information_schema.materialized_views, for example:
      • assertQuery("SELECT COUNT(*) FROM information_schema.materialized_views WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1'", "VALUES 0");
      • assertQuery("SELECT COUNT(*) FROM information_schema.materialized_views WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'", "VALUES 0");
    • If there is a single query used to assert presence before drop (e.g. filtering by table_schema and table_name IN ('test_is_mv1','test_is_mv2')), you may instead assert a single COUNT(*) = 0 over the same predicate after the drops.
  2. In testInformationSchemaMaterializedViewsAfterRefresh:

    • Identify where the materialized views created in that test are dropped.
    • Immediately after the DROP MATERIALIZED VIEW statements (and any table drops if present), add analogous assertions that COUNT(*) = 0 in information_schema.materialized_views for those specific (schema, table_name) combinations.
    • Ensure the table_schema used in the WHERE clause matches whatever schema is used to create the materialized views in that test (likely 'test_schema', but adjust if the test uses a different schema).

These additional changes will ensure all relevant tests validate that information_schema.materialized_views is correctly updated on DROP, not only on CREATE/REFRESH.

assertUpdate("CREATE MATERIALIZED VIEW test_is_mv2 AS SELECT category, SUM(amount) as total FROM test_is_mv_base2 GROUP BY category");

assertQuery(
"SELECT table_name FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_mv1', 'test_is_mv2') " +
"ORDER BY table_name",
"VALUES ('test_is_mv1'), ('test_is_mv2')");

assertQuery(
"SELECT table_catalog, table_schema, table_name, storage_schema, storage_table_name, base_tables " +
"FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1'",
"SELECT 'iceberg', 'test_schema', 'test_is_mv1', 'test_schema', '__mv_storage__test_is_mv1', 'iceberg.test_schema.test_is_mv_base1'");

assertQuery(
"SELECT COUNT(*) FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
"AND view_definition IS NOT NULL AND length(view_definition) > 0",
"SELECT 1");

assertQuery(
"SELECT table_name FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'",
"VALUES ('test_is_mv2')");

assertQuery(
"SELECT COUNT(*) FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
"AND view_owner IS NOT NULL",
"SELECT 1");

assertQuery(
"SELECT COUNT(*) FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
"AND view_security IS NOT NULL",
"SELECT 1");

assertQuery(
"SELECT base_tables FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'",
"VALUES ('iceberg.test_schema.test_is_mv_base2')");

assertUpdate("DROP MATERIALIZED VIEW test_is_mv1");
assertUpdate("DROP MATERIALIZED VIEW test_is_mv2");
assertUpdate("DROP TABLE test_is_mv_base1");
assertUpdate("DROP TABLE test_is_mv_base2");

assertQuery(
"SELECT COUNT(*) FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_mv1', 'test_is_mv2')",
"VALUES 0");
}

@Test
public void testInformationSchemaMaterializedViewsAfterRefresh()
{
assertUpdate("CREATE TABLE test_is_mv_refresh_base (id BIGINT, value BIGINT)");
assertUpdate("INSERT INTO test_is_mv_refresh_base VALUES (1, 100), (2, 200)", 2);
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv_refresh AS SELECT id, value FROM test_is_mv_refresh_base");

assertQuery(
"SELECT freshness_state FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"SELECT 'NOT_MATERIALIZED'");

assertUpdate("REFRESH MATERIALIZED VIEW test_is_mv_refresh", 2);

assertQuery(
"SELECT freshness_state FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"SELECT 'FULLY_MATERIALIZED'");

assertUpdate("INSERT INTO test_is_mv_refresh_base VALUES (3, 300)", 1);

assertQuery(
"SELECT freshness_state FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"SELECT 'PARTIALLY_MATERIALIZED'");

assertUpdate("UPDATE test_is_mv_refresh_base SET value = 250 WHERE id = 2", 1);

assertQuery(
"SELECT freshness_state FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"SELECT 'PARTIALLY_MATERIALIZED'");

assertUpdate("DELETE FROM test_is_mv_refresh_base WHERE id = 1", 1);

assertQuery(
"SELECT freshness_state FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"SELECT 'PARTIALLY_MATERIALIZED'");

assertUpdate("REFRESH MATERIALIZED VIEW test_is_mv_refresh", 2);

assertQuery(
"SELECT freshness_state FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"SELECT 'FULLY_MATERIALIZED'");

assertUpdate("DROP MATERIALIZED VIEW test_is_mv_refresh");
assertUpdate("DROP TABLE test_is_mv_refresh_base");

assertQuery(
"SELECT COUNT(*) FROM information_schema.materialized_views " +
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
"VALUES 0");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class InformationSchemaMetadata
public static final SchemaTableName TABLE_COLUMNS = new SchemaTableName(INFORMATION_SCHEMA, "columns");
public static final SchemaTableName TABLE_TABLES = new SchemaTableName(INFORMATION_SCHEMA, "tables");
public static final SchemaTableName TABLE_VIEWS = new SchemaTableName(INFORMATION_SCHEMA, "views");
public static final SchemaTableName TABLE_MATERIALIZED_VIEWS = new SchemaTableName(INFORMATION_SCHEMA, "materialized_views");
public static final SchemaTableName TABLE_SCHEMATA = new SchemaTableName(INFORMATION_SCHEMA, "schemata");
public static final SchemaTableName TABLE_TABLE_PRIVILEGES = new SchemaTableName(INFORMATION_SCHEMA, "table_privileges");
public static final SchemaTableName TABLE_ROLES = new SchemaTableName(INFORMATION_SCHEMA, "roles");
Expand Down Expand Up @@ -109,6 +110,18 @@ public class InformationSchemaMetadata
.column("view_owner", createUnboundedVarcharType())
.column("view_definition", createUnboundedVarcharType())
.build())
.table(tableMetadataBuilder(TABLE_MATERIALIZED_VIEWS)
.column("table_catalog", createUnboundedVarcharType())
.column("table_schema", createUnboundedVarcharType())
.column("table_name", createUnboundedVarcharType())
.column("view_definition", createUnboundedVarcharType())
.column("view_owner", createUnboundedVarcharType())
.column("view_security", createUnboundedVarcharType())
.column("storage_schema", createUnboundedVarcharType())
.column("storage_table_name", createUnboundedVarcharType())
.column("base_tables", createUnboundedVarcharType())
.column("freshness_state", createUnboundedVarcharType())
.build())
.table(tableMetadataBuilder(TABLE_SCHEMATA)
.column("catalog_name", createUnboundedVarcharType())
.column("schema_name", createUnboundedVarcharType())
Expand Down Expand Up @@ -258,7 +271,7 @@ public ConnectorTableLayoutResult getTableLayoutForConstraint(ConnectorSession s

private boolean isTablesEnumeratingTable(SchemaTableName schemaTableName)
{
return ImmutableSet.of(TABLE_COLUMNS, TABLE_VIEWS, TABLE_TABLES, TABLE_TABLE_PRIVILEGES).contains(schemaTableName);
return ImmutableSet.of(TABLE_COLUMNS, TABLE_VIEWS, TABLE_MATERIALIZED_VIEWS, TABLE_TABLES, TABLE_TABLE_PRIVILEGES).contains(schemaTableName);
}

private Set<QualifiedTablePrefix> calculatePrefixesWithSchemaName(
Expand Down Expand Up @@ -304,8 +317,10 @@ public Set<QualifiedTablePrefix> calculatePrefixesWithTableName(

return prefixes.stream()
.flatMap(prefix -> Stream.concat(
metadata.listTables(session, prefix).stream(),
metadata.listViews(session, prefix).stream()))
Stream.concat(
metadata.listTables(session, prefix).stream(),
metadata.listViews(session, prefix).stream()),
metadata.listMaterializedViews(session, prefix).stream()))
.filter(objectName -> !predicate.isPresent() || predicate.get().test(asFixedValues(objectName)))
.map(value -> toQualifiedTablePrefix(new QualifiedObjectName(
value.getCatalogName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import com.facebook.presto.spi.ConnectorSession;
import com.facebook.presto.spi.ConnectorSplit;
import com.facebook.presto.spi.FixedPageSource;
import com.facebook.presto.spi.MaterializedViewDefinition;
import com.facebook.presto.spi.MaterializedViewStatus;
import com.facebook.presto.spi.SchemaTableName;
import com.facebook.presto.spi.SplitContext;
import com.facebook.presto.spi.analyzer.ViewDefinition;
Expand All @@ -57,13 +59,15 @@
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_APPLICABLE_ROLES;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_COLUMNS;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_ENABLED_ROLES;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_MATERIALIZED_VIEWS;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_ROLES;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_SCHEMATA;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_TABLES;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_TABLE_PRIVILEGES;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_VIEWS;
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.informationSchemaTableColumns;
import static com.facebook.presto.connector.system.jdbc.ColumnJdbcTable.decimalDigits;
import static com.facebook.presto.metadata.MetadataListing.listMaterializedViews;
import static com.facebook.presto.metadata.MetadataListing.listSchemas;
import static com.facebook.presto.metadata.MetadataListing.listTableColumns;
import static com.facebook.presto.metadata.MetadataListing.listTablePrivileges;
Expand Down Expand Up @@ -138,6 +142,9 @@ public InternalTable getInformationSchemaTable(Session session, String catalog,
if (table.equals(TABLE_VIEWS)) {
return buildViews(session, prefixes);
}
if (table.equals(TABLE_MATERIALIZED_VIEWS)) {
return buildMaterializedViews(session, prefixes);
}
if (table.equals(TABLE_SCHEMATA)) {
return buildSchemata(session, catalog);
}
Expand Down Expand Up @@ -195,10 +202,19 @@ private InternalTable buildTables(Session session, Set<QualifiedTablePrefix> pre
for (QualifiedTablePrefix prefix : prefixes) {
Set<SchemaTableName> tables = listTables(session, metadata, accessControl, prefix);
Set<SchemaTableName> views = listViews(session, metadata, accessControl, prefix);
Set<SchemaTableName> materializedViews = listMaterializedViews(session, metadata, accessControl, prefix);

for (SchemaTableName name : union(tables, views)) {
// if table and view names overlap, the view wins
String type = views.contains(name) ? "VIEW" : "BASE TABLE";
for (SchemaTableName name : union(union(tables, views), materializedViews)) {
String type;
if (materializedViews.contains(name)) {
type = "MATERIALIZED VIEW";
}
else if (views.contains(name)) {
type = "VIEW";
}
else {
type = "BASE TABLE";
}
table.add(
prefix.getCatalogName(),
name.getSchemaName(),
Expand Down Expand Up @@ -247,6 +263,39 @@ private InternalTable buildViews(Session session, Set<QualifiedTablePrefix> pref
return table.build();
}

private InternalTable buildMaterializedViews(Session session, Set<QualifiedTablePrefix> prefixes)
{
InternalTable.Builder table = InternalTable.builder(informationSchemaTableColumns(TABLE_MATERIALIZED_VIEWS));

for (QualifiedTablePrefix prefix : prefixes) {
for (Entry<QualifiedObjectName, MaterializedViewDefinition> entry : metadata.getMaterializedViews(session, prefix).entrySet()) {
QualifiedObjectName viewName = entry.getKey();
MaterializedViewDefinition definition = entry.getValue();

String baseTablesStr = definition.getBaseTables().stream()
.map(baseTable -> viewName.getCatalogName() + "." + baseTable.getSchemaName() + "." + baseTable.getTableName())
.collect(java.util.stream.Collectors.joining(", "));

MaterializedViewStatus status = metadata.getMaterializedViewStatus(session, viewName);
String freshnessState = status.getMaterializedViewState().name();

table.add(
viewName.getCatalogName(),
viewName.getSchemaName(),
viewName.getObjectName(),
definition.getOriginalSql(),
definition.getOwner().orElse(null),
definition.getSecurityMode().map(Object::toString).orElse(null),
definition.getSchema(),
definition.getTable(),
baseTablesStr,
freshnessState);
}
}

return table.build();
}

private InternalTable buildSchemata(Session session, String catalogName)
{
InternalTable.Builder table = InternalTable.builder(informationSchemaTableColumns(TABLE_SCHEMATA));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import static com.facebook.presto.connector.system.jdbc.FilterUtil.stringFilter;
import static com.facebook.presto.connector.system.jdbc.FilterUtil.tablePrefix;
import static com.facebook.presto.metadata.MetadataListing.listCatalogs;
import static com.facebook.presto.metadata.MetadataListing.listMaterializedViews;
import static com.facebook.presto.metadata.MetadataListing.listTables;
import static com.facebook.presto.metadata.MetadataListing.listViews;
import static com.facebook.presto.metadata.MetadataUtil.TableMetadataBuilder.tableMetadataBuilder;
Expand Down Expand Up @@ -97,9 +98,17 @@ public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, Connect
}
}

Set<SchemaTableName> materializedViews = ImmutableSet.of();
if (FilterUtil.emptyOrEquals(typeFilter, "MATERIALIZED VIEW")) {
materializedViews = ImmutableSet.copyOf(listMaterializedViews(session, metadata, accessControl, prefix));
for (SchemaTableName name : materializedViews) {
table.addRow(tableRow(catalog, name, "MATERIALIZED VIEW"));
}
}

if (FilterUtil.emptyOrEquals(typeFilter, "TABLE")) {
for (SchemaTableName name : listTables(session, metadata, accessControl, prefix)) {
if (!views.contains(name)) {
if (!views.contains(name) && !materializedViews.contains(name)) {
table.addRow(tableRow(catalog, name, "TABLE"));
}
}
Expand Down
Loading
Loading