Skip to content

Commit 0e4171e

Browse files
committed
Add materialized views to information_schema
1 parent 42c6d5f commit 0e4171e

File tree

14 files changed

+467
-15
lines changed

14 files changed

+467
-15
lines changed

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,24 @@ public void createMaterializedView(
14941494
}
14951495
}
14961496

1497+
@Override
1498+
public List<SchemaTableName> listMaterializedViews(ConnectorSession session, String schemaName)
1499+
{
1500+
ImmutableList.Builder<SchemaTableName> materializedViews = ImmutableList.builder();
1501+
1502+
List<SchemaTableName> views = listViews(session, Optional.of(schemaName));
1503+
1504+
for (SchemaTableName viewName : views) {
1505+
View icebergView = getIcebergView(session, viewName);
1506+
Map<String, String> properties = icebergView.properties();
1507+
if (properties.containsKey(PRESTO_MATERIALIZED_VIEW_FORMAT_VERSION)) {
1508+
materializedViews.add(viewName);
1509+
}
1510+
}
1511+
1512+
return materializedViews.build();
1513+
}
1514+
14971515
@Override
14981516
public Optional<MaterializedViewDefinition> getMaterializedView(ConnectorSession session, SchemaTableName viewName)
14991517
{
@@ -1599,8 +1617,7 @@ public void dropMaterializedView(ConnectorSession session, SchemaTableName viewN
15991617
@Override
16001618
public MaterializedViewStatus getMaterializedViewStatus(
16011619
ConnectorSession session,
1602-
SchemaTableName materializedViewName,
1603-
TupleDomain<String> baseQueryDomain)
1620+
SchemaTableName materializedViewName)
16041621
{
16051622
Optional<MaterializedViewDefinition> definition = getMaterializedView(session, materializedViewName);
16061623
if (definition.isEmpty()) {

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

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,9 @@ public void testMaterializedViewMetadata()
188188

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

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

194195
assertUpdate("DROP MATERIALIZED VIEW test_mv_metadata");
195196
assertUpdate("DROP TABLE test_mv_metadata_base");
@@ -1400,4 +1401,113 @@ public void testCreateMaterializedViewWithSameNameAsExistingTable()
14001401
assertUpdate("DROP TABLE existing_table_name");
14011402
assertUpdate("DROP TABLE test_mv_base");
14021403
}
1404+
1405+
@Test
1406+
public void testInformationSchemaMaterializedViews()
1407+
{
1408+
assertUpdate("CREATE TABLE test_is_mv_base1 (id BIGINT, name VARCHAR, value BIGINT)");
1409+
assertUpdate("CREATE TABLE test_is_mv_base2 (category VARCHAR, amount BIGINT)");
1410+
1411+
assertUpdate("INSERT INTO test_is_mv_base1 VALUES (1, 'Alice', 100), (2, 'Bob', 200)", 2);
1412+
assertUpdate("INSERT INTO test_is_mv_base2 VALUES ('A', 50), ('B', 75)", 2);
1413+
1414+
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv1 AS SELECT id, name, value FROM test_is_mv_base1 WHERE id > 0");
1415+
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv2 AS SELECT category, SUM(amount) as total FROM test_is_mv_base2 GROUP BY category");
1416+
1417+
assertQuery(
1418+
"SELECT table_name FROM information_schema.materialized_views " +
1419+
"WHERE table_schema = 'test_schema' AND table_name IN ('test_is_mv1', 'test_is_mv2') " +
1420+
"ORDER BY table_name",
1421+
"VALUES ('test_is_mv1'), ('test_is_mv2')");
1422+
1423+
assertQuery(
1424+
"SELECT table_catalog, table_schema, table_name, storage_schema, storage_table_name, base_tables " +
1425+
"FROM information_schema.materialized_views " +
1426+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1'",
1427+
"SELECT 'iceberg', 'test_schema', 'test_is_mv1', 'test_schema', '__mv_storage__test_is_mv1', 'test_schema.test_is_mv_base1'");
1428+
1429+
assertQuery(
1430+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1431+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
1432+
"AND view_definition IS NOT NULL AND length(view_definition) > 0",
1433+
"SELECT 1");
1434+
1435+
assertQuery(
1436+
"SELECT table_name FROM information_schema.materialized_views " +
1437+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'",
1438+
"VALUES ('test_is_mv2')");
1439+
1440+
assertQuery(
1441+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1442+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
1443+
"AND view_owner IS NOT NULL",
1444+
"SELECT 1");
1445+
1446+
assertQuery(
1447+
"SELECT COUNT(*) FROM information_schema.materialized_views " +
1448+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv1' " +
1449+
"AND view_security IS NOT NULL",
1450+
"SELECT 1");
1451+
1452+
assertQuery(
1453+
"SELECT base_tables FROM information_schema.materialized_views " +
1454+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv2'",
1455+
"VALUES ('test_schema.test_is_mv_base2')");
1456+
1457+
assertUpdate("DROP MATERIALIZED VIEW test_is_mv1");
1458+
assertUpdate("DROP MATERIALIZED VIEW test_is_mv2");
1459+
assertUpdate("DROP TABLE test_is_mv_base1");
1460+
assertUpdate("DROP TABLE test_is_mv_base2");
1461+
}
1462+
1463+
@Test
1464+
public void testInformationSchemaMaterializedViewsAfterRefresh()
1465+
{
1466+
assertUpdate("CREATE TABLE test_is_mv_refresh_base (id BIGINT, value BIGINT)");
1467+
assertUpdate("INSERT INTO test_is_mv_refresh_base VALUES (1, 100), (2, 200)", 2);
1468+
assertUpdate("CREATE MATERIALIZED VIEW test_is_mv_refresh AS SELECT id, value FROM test_is_mv_refresh_base");
1469+
1470+
assertQuery(
1471+
"SELECT freshness_state FROM information_schema.materialized_views " +
1472+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1473+
"SELECT 'NOT_MATERIALIZED'");
1474+
1475+
assertUpdate("REFRESH MATERIALIZED VIEW test_is_mv_refresh", 2);
1476+
1477+
assertQuery(
1478+
"SELECT freshness_state FROM information_schema.materialized_views " +
1479+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1480+
"SELECT 'FULLY_MATERIALIZED'");
1481+
1482+
assertUpdate("INSERT INTO test_is_mv_refresh_base VALUES (3, 300)", 1);
1483+
1484+
assertQuery(
1485+
"SELECT freshness_state FROM information_schema.materialized_views " +
1486+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1487+
"SELECT 'PARTIALLY_MATERIALIZED'");
1488+
1489+
assertUpdate("UPDATE test_is_mv_refresh_base SET value = 250 WHERE id = 2", 1);
1490+
1491+
assertQuery(
1492+
"SELECT freshness_state FROM information_schema.materialized_views " +
1493+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1494+
"SELECT 'PARTIALLY_MATERIALIZED'");
1495+
1496+
assertUpdate("DELETE FROM test_is_mv_refresh_base WHERE id = 1", 1);
1497+
1498+
assertQuery(
1499+
"SELECT freshness_state FROM information_schema.materialized_views " +
1500+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1501+
"SELECT 'PARTIALLY_MATERIALIZED'");
1502+
1503+
assertUpdate("REFRESH MATERIALIZED VIEW test_is_mv_refresh", 2);
1504+
1505+
assertQuery(
1506+
"SELECT freshness_state FROM information_schema.materialized_views " +
1507+
"WHERE table_schema = 'test_schema' AND table_name = 'test_is_mv_refresh'",
1508+
"SELECT 'FULLY_MATERIALIZED'");
1509+
1510+
assertUpdate("DROP MATERIALIZED VIEW test_is_mv_refresh");
1511+
assertUpdate("DROP TABLE test_is_mv_refresh_base");
1512+
}
14031513
}

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(),

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

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import com.facebook.presto.spi.ConnectorSession;
3232
import com.facebook.presto.spi.ConnectorSplit;
3333
import com.facebook.presto.spi.FixedPageSource;
34+
import com.facebook.presto.spi.MaterializedViewDefinition;
35+
import com.facebook.presto.spi.MaterializedViewStatus;
3436
import com.facebook.presto.spi.SchemaTableName;
3537
import com.facebook.presto.spi.SplitContext;
3638
import com.facebook.presto.spi.analyzer.ViewDefinition;
@@ -57,13 +59,15 @@
5759
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_APPLICABLE_ROLES;
5860
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_COLUMNS;
5961
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_ENABLED_ROLES;
62+
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_MATERIALIZED_VIEWS;
6063
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_ROLES;
6164
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_SCHEMATA;
6265
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_TABLES;
6366
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_TABLE_PRIVILEGES;
6467
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.TABLE_VIEWS;
6568
import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.informationSchemaTableColumns;
6669
import static com.facebook.presto.connector.system.jdbc.ColumnJdbcTable.decimalDigits;
70+
import static com.facebook.presto.metadata.MetadataListing.listMaterializedViews;
6771
import static com.facebook.presto.metadata.MetadataListing.listSchemas;
6872
import static com.facebook.presto.metadata.MetadataListing.listTableColumns;
6973
import static com.facebook.presto.metadata.MetadataListing.listTablePrivileges;
@@ -138,6 +142,9 @@ public InternalTable getInformationSchemaTable(Session session, String catalog,
138142
if (table.equals(TABLE_VIEWS)) {
139143
return buildViews(session, prefixes);
140144
}
145+
if (table.equals(TABLE_MATERIALIZED_VIEWS)) {
146+
return buildMaterializedViews(session, prefixes);
147+
}
141148
if (table.equals(TABLE_SCHEMATA)) {
142149
return buildSchemata(session, catalog);
143150
}
@@ -195,10 +202,19 @@ private InternalTable buildTables(Session session, Set<QualifiedTablePrefix> pre
195202
for (QualifiedTablePrefix prefix : prefixes) {
196203
Set<SchemaTableName> tables = listTables(session, metadata, accessControl, prefix);
197204
Set<SchemaTableName> views = listViews(session, metadata, accessControl, prefix);
205+
Set<SchemaTableName> materializedViews = listMaterializedViews(session, metadata, accessControl, prefix);
198206

199-
for (SchemaTableName name : union(tables, views)) {
200-
// if table and view names overlap, the view wins
201-
String type = views.contains(name) ? "VIEW" : "BASE TABLE";
207+
for (SchemaTableName name : union(union(tables, views), materializedViews)) {
208+
String type;
209+
if (materializedViews.contains(name)) {
210+
type = "MATERIALIZED VIEW";
211+
}
212+
else if (views.contains(name)) {
213+
type = "VIEW";
214+
}
215+
else {
216+
type = "BASE TABLE";
217+
}
202218
table.add(
203219
prefix.getCatalogName(),
204220
name.getSchemaName(),
@@ -247,6 +263,39 @@ private InternalTable buildViews(Session session, Set<QualifiedTablePrefix> pref
247263
return table.build();
248264
}
249265

266+
private InternalTable buildMaterializedViews(Session session, Set<QualifiedTablePrefix> prefixes)
267+
{
268+
InternalTable.Builder table = InternalTable.builder(informationSchemaTableColumns(TABLE_MATERIALIZED_VIEWS));
269+
270+
for (QualifiedTablePrefix prefix : prefixes) {
271+
for (Entry<QualifiedObjectName, MaterializedViewDefinition> entry : metadata.getMaterializedViews(session, prefix).entrySet()) {
272+
QualifiedObjectName viewName = entry.getKey();
273+
MaterializedViewDefinition definition = entry.getValue();
274+
275+
String baseTablesStr = definition.getBaseTables().stream()
276+
.map(baseTable -> viewName.getCatalogName() + "." + baseTable.getSchemaName() + "." + baseTable.getTableName())
277+
.collect(java.util.stream.Collectors.joining(", "));
278+
279+
MaterializedViewStatus status = metadata.getMaterializedViewStatus(session, viewName);
280+
String freshnessState = status.getMaterializedViewState().name();
281+
282+
table.add(
283+
viewName.getCatalogName(),
284+
viewName.getSchemaName(),
285+
viewName.getObjectName(),
286+
definition.getOriginalSql(),
287+
definition.getOwner().orElse(null),
288+
definition.getSecurityMode().map(Object::toString).orElse(null),
289+
definition.getSchema(),
290+
definition.getTable(),
291+
baseTablesStr,
292+
freshnessState);
293+
}
294+
}
295+
296+
return table.build();
297+
}
298+
250299
private InternalTable buildSchemata(Session session, String catalogName)
251300
{
252301
InternalTable.Builder table = InternalTable.builder(informationSchemaTableColumns(TABLE_SCHEMATA));

presto-main-base/src/main/java/com/facebook/presto/connector/system/jdbc/TableJdbcTable.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import static com.facebook.presto.connector.system.jdbc.FilterUtil.stringFilter;
3838
import static com.facebook.presto.connector.system.jdbc.FilterUtil.tablePrefix;
3939
import static com.facebook.presto.metadata.MetadataListing.listCatalogs;
40+
import static com.facebook.presto.metadata.MetadataListing.listMaterializedViews;
4041
import static com.facebook.presto.metadata.MetadataListing.listTables;
4142
import static com.facebook.presto.metadata.MetadataListing.listViews;
4243
import static com.facebook.presto.metadata.MetadataUtil.TableMetadataBuilder.tableMetadataBuilder;
@@ -97,9 +98,17 @@ public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, Connect
9798
}
9899
}
99100

101+
Set<SchemaTableName> materializedViews = ImmutableSet.of();
102+
if (FilterUtil.emptyOrEquals(typeFilter, "MATERIALIZED VIEW")) {
103+
materializedViews = ImmutableSet.copyOf(listMaterializedViews(session, metadata, accessControl, prefix));
104+
for (SchemaTableName name : materializedViews) {
105+
table.addRow(tableRow(catalog, name, "MATERIALIZED VIEW"));
106+
}
107+
}
108+
100109
if (FilterUtil.emptyOrEquals(typeFilter, "TABLE")) {
101110
for (SchemaTableName name : listTables(session, metadata, accessControl, prefix)) {
102-
if (!views.contains(name)) {
111+
if (!views.contains(name) && !materializedViews.contains(name)) {
103112
table.addRow(tableRow(catalog, name, "TABLE"));
104113
}
105114
}

presto-main-base/src/main/java/com/facebook/presto/metadata/DelegatingMetadataManager.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.facebook.presto.spi.ConnectorTableMetadata;
2727
import com.facebook.presto.spi.Constraint;
2828
import com.facebook.presto.spi.MaterializedViewDefinition;
29+
import com.facebook.presto.spi.MaterializedViewStatus;
2930
import com.facebook.presto.spi.NewTableLayout;
3031
import com.facebook.presto.spi.SystemTable;
3132
import com.facebook.presto.spi.TableHandle;
@@ -449,6 +450,26 @@ public Map<QualifiedObjectName, ViewDefinition> getViews(Session session, Qualif
449450
return delegate.getViews(session, prefix);
450451
}
451452

453+
@Override
454+
public List<QualifiedObjectName> listMaterializedViews(Session session, QualifiedTablePrefix prefix)
455+
{
456+
return delegate.listMaterializedViews(session, prefix);
457+
}
458+
459+
@Override
460+
public Map<QualifiedObjectName, MaterializedViewDefinition> getMaterializedViews(
461+
Session session,
462+
QualifiedTablePrefix prefix)
463+
{
464+
return delegate.getMaterializedViews(session, prefix);
465+
}
466+
467+
@Override
468+
public MaterializedViewStatus getMaterializedViewStatus(Session session, QualifiedObjectName viewName)
469+
{
470+
return delegate.getMaterializedViewStatus(session, viewName);
471+
}
472+
452473
@Override
453474
public void createView(Session session, String catalogName, ConnectorTableMetadata viewMetadata, String viewData, boolean replace)
454475
{

0 commit comments

Comments
 (0)