Skip to content

Commit e948a9d

Browse files
authored
feat: Add materialized views to information_schema (#26688)
1 parent 18323c3 commit e948a9d

File tree

17 files changed

+510
-38
lines changed

17 files changed

+510
-38
lines changed

presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergAbstractMetadata.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,24 @@ public void createMaterializedView(
14931493
}
14941494
}
14951495

1496+
@Override
1497+
public List<SchemaTableName> listMaterializedViews(ConnectorSession session, String schemaName)
1498+
{
1499+
ImmutableList.Builder<SchemaTableName> materializedViews = ImmutableList.builder();
1500+
1501+
List<SchemaTableName> views = listViews(session, Optional.of(schemaName));
1502+
1503+
for (SchemaTableName viewName : views) {
1504+
View icebergView = getIcebergView(session, viewName);
1505+
Map<String, String> properties = icebergView.properties();
1506+
if (properties.containsKey(PRESTO_MATERIALIZED_VIEW_FORMAT_VERSION)) {
1507+
materializedViews.add(viewName);
1508+
}
1509+
}
1510+
1511+
return materializedViews.build();
1512+
}
1513+
14961514
@Override
14971515
public Optional<MaterializedViewDefinition> getMaterializedView(ConnectorSession session, SchemaTableName viewName)
14981516
{

presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergHiveMetadata.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,12 @@ public List<SchemaTableName> listViews(ConnectorSession session, Optional<String
474474
return tableNames.build();
475475
}
476476

477+
@Override
478+
public List<SchemaTableName> listMaterializedViews(ConnectorSession session, String schemaName)
479+
{
480+
return ImmutableList.of();
481+
}
482+
477483
@Override
478484
public Map<SchemaTableName, ConnectorViewDefinition> getViews(ConnectorSession session, SchemaTablePrefix prefix)
479485
{

presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergNativeMetadata.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,21 @@ public List<SchemaTableName> listViews(ConnectorSession session, Optional<String
259259
ImmutableList.Builder<SchemaTableName> tableNames = ImmutableList.builder();
260260
Catalog catalog = catalogFactory.getCatalog(session);
261261
if (catalog instanceof ViewCatalog) {
262+
ViewCatalog viewCatalog = (ViewCatalog) catalog;
262263
for (String schema : listSchemas(session, schemaName.orElse(null))) {
263264
try {
264-
for (TableIdentifier tableIdentifier : ((ViewCatalog) catalog).listViews(
265+
for (TableIdentifier tableIdentifier : viewCatalog.listViews(
265266
toIcebergNamespace(Optional.ofNullable(schema), catalogFactory.isNestedNamespaceEnabled()))) {
266-
tableNames.add(new SchemaTableName(schema, tableIdentifier.name()));
267+
// Exclude materialized views from the list of views
268+
try {
269+
View view = viewCatalog.loadView(tableIdentifier);
270+
if (!view.properties().containsKey(PRESTO_MATERIALIZED_VIEW_FORMAT_VERSION)) {
271+
tableNames.add(new SchemaTableName(schema, tableIdentifier.name()));
272+
}
273+
}
274+
catch (IllegalArgumentException e) {
275+
// Ignore illegal view names
276+
}
267277
}
268278
}
269279
catch (NoSuchNamespaceException e) {
@@ -302,7 +312,7 @@ public Map<SchemaTableName, ConnectorViewDefinition> getViews(ConnectorSession s
302312
if (((ViewCatalog) catalog).viewExists(viewIdentifier)) {
303313
View view = ((ViewCatalog) catalog).loadView(viewIdentifier);
304314
// Skip materialized views
305-
if (view.properties().containsKey(PRESTO_MATERIALIZED_VIEW_ORIGINAL_SQL)) {
315+
if (view.properties().containsKey(PRESTO_MATERIALIZED_VIEW_FORMAT_VERSION)) {
306316
continue;
307317
}
308318
verifyAndPopulateViews(view, schemaTableName, view.sqlFor(VIEW_DIALECT).sql(), views);
@@ -311,15 +321,39 @@ public Map<SchemaTableName, ConnectorViewDefinition> getViews(ConnectorSession s
311321
catch (IllegalArgumentException e) {
312322
// Ignore illegal view names
313323
}
314-
catch (Exception e) {
315-
// Ignore views that can't be loaded (e.g., if listViews returned a table name by mistake)
316-
// This can happen if the catalog's listViews() implementation is buggy
317-
}
318324
}
319325
}
320326
return views.build();
321327
}
322328

329+
@Override
330+
public List<SchemaTableName> listMaterializedViews(ConnectorSession session, String schemaName)
331+
{
332+
ImmutableList.Builder<SchemaTableName> materializedViews = ImmutableList.builder();
333+
Catalog catalog = catalogFactory.getCatalog(session);
334+
if (catalog instanceof ViewCatalog) {
335+
ViewCatalog viewCatalog = (ViewCatalog) catalog;
336+
try {
337+
for (TableIdentifier tableIdentifier : viewCatalog.listViews(
338+
toIcebergNamespace(Optional.ofNullable(schemaName), catalogFactory.isNestedNamespaceEnabled()))) {
339+
try {
340+
View view = viewCatalog.loadView(tableIdentifier);
341+
if (view.properties().containsKey(PRESTO_MATERIALIZED_VIEW_FORMAT_VERSION)) {
342+
materializedViews.add(new SchemaTableName(schemaName, tableIdentifier.name()));
343+
}
344+
}
345+
catch (IllegalArgumentException e) {
346+
// Ignore illegal view names
347+
}
348+
}
349+
}
350+
catch (NoSuchNamespaceException e) {
351+
// ignore
352+
}
353+
}
354+
return materializedViews.build();
355+
}
356+
323357
@Override
324358
public void dropView(ConnectorSession session, SchemaTableName viewName)
325359
{

presto-iceberg/src/test/java/com/facebook/presto/iceberg/TestIcebergMaterializedViews.java

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ public void testMaterializedViewMetadata()
189189

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

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

195196
assertUpdate("DROP MATERIALIZED VIEW test_mv_metadata");
196197
assertUpdate("DROP TABLE test_mv_metadata_base");
@@ -1451,4 +1452,147 @@ public void testCreateMaterializedViewWithSameNameAsExistingTable()
14511452
assertUpdate("DROP TABLE existing_table_name");
14521453
assertUpdate("DROP TABLE test_mv_base");
14531454
}
1455+
1456+
@Test
1457+
public void testInformationSchemaMaterializedViews()
1458+
{
1459+
assertUpdate("CREATE TABLE test_is_mv_base1 (id BIGINT, name VARCHAR, value BIGINT)");
1460+
assertUpdate("CREATE TABLE test_is_mv_base2 (category VARCHAR, amount BIGINT)");
1461+
1462+
assertUpdate("INSERT INTO test_is_mv_base1 VALUES (1, 'Alice', 100), (2, 'Bob', 200)", 2);
1463+
assertUpdate("INSERT INTO test_is_mv_base2 VALUES ('A', 50), ('B', 75)", 2);
1464+
1465+
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv1 AS SELECT id, name, value FROM test_is_mv_base1 WHERE id > 0");
1466+
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv2 AS SELECT category, SUM(amount) as total FROM test_is_mv_base2 GROUP BY category");
1467+
1468+
assertQuery(
1469+
"SELECT table_name FROM information_schema.materialized_views " +
1470+
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_mv1', 'test_is_mv2') " +
1471+
"ORDER BY table_name",
1472+
"VALUES ('test_is_mv1'), ('test_is_mv2')");
1473+
1474+
assertQuery(
1475+
"SELECT table_catalog, table_schema, table_name, storage_schema, storage_table_name, base_tables " +
1476+
"FROM information_schema.materialized_views " +
1477+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1'",
1478+
"SELECT 'iceberg', 'test_schema', 'test_is_mv1', 'test_schema', '__mv_storage__test_is_mv1', 'iceberg.test_schema.test_is_mv_base1'");
1479+
1480+
assertQuery(
1481+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1482+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
1483+
"AND view_definition IS NOT NULL AND length(view_definition) > 0",
1484+
"SELECT 1");
1485+
1486+
assertQuery(
1487+
"SELECT table_name FROM information_schema.materialized_views " +
1488+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'",
1489+
"VALUES ('test_is_mv2')");
1490+
1491+
assertQuery(
1492+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1493+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
1494+
"AND view_owner IS NOT NULL",
1495+
"SELECT 1");
1496+
1497+
assertQuery(
1498+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1499+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
1500+
"AND view_security IS NOT NULL",
1501+
"SELECT 1");
1502+
1503+
assertQuery(
1504+
"SELECT base_tables FROM information_schema.materialized_views " +
1505+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'",
1506+
"VALUES ('iceberg.test_schema.test_is_mv_base2')");
1507+
1508+
assertUpdate("DROP MATERIALIZED VIEW test_is_mv1");
1509+
assertUpdate("DROP MATERIALIZED VIEW test_is_mv2");
1510+
assertUpdate("DROP TABLE test_is_mv_base1");
1511+
assertUpdate("DROP TABLE test_is_mv_base2");
1512+
1513+
assertQuery(
1514+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1515+
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_mv1', 'test_is_mv2')",
1516+
"VALUES 0");
1517+
}
1518+
1519+
@Test
1520+
public void testInformationSchemaTablesWithMaterializedViews()
1521+
{
1522+
assertUpdate("CREATE TABLE test_is_tables_base (id BIGINT, name VARCHAR)");
1523+
assertUpdate("CREATE VIEW test_is_tables_view AS SELECT id, name FROM test_is_tables_base");
1524+
assertUpdate("CREATE MATERIALIZED VIEW test_is_tables_mv AS SELECT id, name FROM test_is_tables_base");
1525+
1526+
assertQuery(
1527+
"SELECT table_name, table_type FROM information_schema.tables " +
1528+
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_tables_base', 'test_is_tables_view', 'test_is_tables_mv') " +
1529+
"ORDER BY table_name",
1530+
"VALUES ('test_is_tables_base', 'BASE TABLE'), ('test_is_tables_mv', 'MATERIALIZED VIEW'), ('test_is_tables_view', 'VIEW')");
1531+
1532+
assertQuery(
1533+
"SELECT table_name FROM information_schema.views " +
1534+
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_tables_view', 'test_is_tables_mv') " +
1535+
"ORDER BY table_name",
1536+
"VALUES ('test_is_tables_view')");
1537+
1538+
assertUpdate("DROP MATERIALIZED VIEW test_is_tables_mv");
1539+
assertUpdate("DROP VIEW test_is_tables_view");
1540+
assertUpdate("DROP TABLE test_is_tables_base");
1541+
}
1542+
1543+
@Test
1544+
public void testInformationSchemaMaterializedViewsAfterRefresh()
1545+
{
1546+
assertUpdate("CREATE TABLE test_is_mv_refresh_base (id BIGINT, value BIGINT)");
1547+
assertUpdate("INSERT INTO test_is_mv_refresh_base VALUES (1, 100), (2, 200)", 2);
1548+
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv_refresh AS SELECT id, value FROM test_is_mv_refresh_base");
1549+
1550+
assertQuery(
1551+
"SELECT freshness_state FROM information_schema.materialized_views " +
1552+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1553+
"SELECT 'NOT_MATERIALIZED'");
1554+
1555+
assertUpdate("REFRESH MATERIALIZED VIEW test_is_mv_refresh", 2);
1556+
1557+
assertQuery(
1558+
"SELECT freshness_state FROM information_schema.materialized_views " +
1559+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1560+
"SELECT 'FULLY_MATERIALIZED'");
1561+
1562+
assertUpdate("INSERT INTO test_is_mv_refresh_base VALUES (3, 300)", 1);
1563+
1564+
assertQuery(
1565+
"SELECT freshness_state FROM information_schema.materialized_views " +
1566+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1567+
"SELECT 'PARTIALLY_MATERIALIZED'");
1568+
1569+
assertUpdate("UPDATE test_is_mv_refresh_base SET value = 250 WHERE id = 2", 1);
1570+
1571+
assertQuery(
1572+
"SELECT freshness_state FROM information_schema.materialized_views " +
1573+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1574+
"SELECT 'PARTIALLY_MATERIALIZED'");
1575+
1576+
assertUpdate("DELETE FROM test_is_mv_refresh_base WHERE id = 1", 1);
1577+
1578+
assertQuery(
1579+
"SELECT freshness_state FROM information_schema.materialized_views " +
1580+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1581+
"SELECT 'PARTIALLY_MATERIALIZED'");
1582+
1583+
assertUpdate("REFRESH MATERIALIZED VIEW test_is_mv_refresh", 2);
1584+
1585+
assertQuery(
1586+
"SELECT freshness_state FROM information_schema.materialized_views " +
1587+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1588+
"SELECT 'FULLY_MATERIALIZED'");
1589+
1590+
assertUpdate("DROP MATERIALIZED VIEW test_is_mv_refresh");
1591+
assertUpdate("DROP TABLE test_is_mv_refresh_base");
1592+
1593+
assertQuery(
1594+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1595+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1596+
"VALUES 0");
1597+
}
14541598
}

presto-main-base/src/main/java/com/facebook/presto/connector/informationSchema/InformationSchemaMetadata.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class InformationSchemaMetadata
7474
public static final SchemaTableName TABLE_COLUMNS = new SchemaTableName(INFORMATION_SCHEMA, "columns");
7575
public static final SchemaTableName TABLE_TABLES = new SchemaTableName(INFORMATION_SCHEMA, "tables");
7676
public static final SchemaTableName TABLE_VIEWS = new SchemaTableName(INFORMATION_SCHEMA, "views");
77+
public static final SchemaTableName TABLE_MATERIALIZED_VIEWS = new SchemaTableName(INFORMATION_SCHEMA, "materialized_views");
7778
public static final SchemaTableName TABLE_SCHEMATA = new SchemaTableName(INFORMATION_SCHEMA, "schemata");
7879
public static final SchemaTableName TABLE_TABLE_PRIVILEGES = new SchemaTableName(INFORMATION_SCHEMA, "table_privileges");
7980
public static final SchemaTableName TABLE_ROLES = new SchemaTableName(INFORMATION_SCHEMA, "roles");
@@ -109,6 +110,18 @@ public class InformationSchemaMetadata
109110
.column("view_owner", createUnboundedVarcharType())
110111
.column("view_definition", createUnboundedVarcharType())
111112
.build())
113+
.table(tableMetadataBuilder(TABLE_MATERIALIZED_VIEWS)
114+
.column("table_catalog", createUnboundedVarcharType())
115+
.column("table_schema", createUnboundedVarcharType())
116+
.column("table_name", createUnboundedVarcharType())
117+
.column("view_definition", createUnboundedVarcharType())
118+
.column("view_owner", createUnboundedVarcharType())
119+
.column("view_security", createUnboundedVarcharType())
120+
.column("storage_schema", createUnboundedVarcharType())
121+
.column("storage_table_name", createUnboundedVarcharType())
122+
.column("base_tables", createUnboundedVarcharType())
123+
.column("freshness_state", createUnboundedVarcharType())
124+
.build())
112125
.table(tableMetadataBuilder(TABLE_SCHEMATA)
113126
.column("catalog_name", createUnboundedVarcharType())
114127
.column("schema_name", createUnboundedVarcharType())
@@ -258,7 +271,7 @@ public ConnectorTableLayoutResult getTableLayoutForConstraint(ConnectorSession s
258271

259272
private boolean isTablesEnumeratingTable(SchemaTableName schemaTableName)
260273
{
261-
return ImmutableSet.of(TABLE_COLUMNS, TABLE_VIEWS, TABLE_TABLES, TABLE_TABLE_PRIVILEGES).contains(schemaTableName);
274+
return ImmutableSet.of(TABLE_COLUMNS, TABLE_VIEWS, TABLE_MATERIALIZED_VIEWS, TABLE_TABLES, TABLE_TABLE_PRIVILEGES).contains(schemaTableName);
262275
}
263276

264277
private Set<QualifiedTablePrefix> calculatePrefixesWithSchemaName(
@@ -304,8 +317,10 @@ public Set<QualifiedTablePrefix> calculatePrefixesWithTableName(
304317

305318
return prefixes.stream()
306319
.flatMap(prefix -> Stream.concat(
307-
metadata.listTables(session, prefix).stream(),
308-
metadata.listViews(session, prefix).stream()))
320+
Stream.concat(
321+
metadata.listTables(session, prefix).stream(),
322+
metadata.listViews(session, prefix).stream()),
323+
metadata.listMaterializedViews(session, prefix).stream()))
309324
.filter(objectName -> !predicate.isPresent() || predicate.get().test(asFixedValues(objectName)))
310325
.map(value -> toQualifiedTablePrefix(new QualifiedObjectName(
311326
value.getCatalogName(),

0 commit comments

Comments
 (0)