From df2503a5b92917ce91def7cf2dbffcbab2624479 Mon Sep 17 00:00:00 2001 From: Piotr Rzysko Date: Tue, 18 Nov 2025 07:49:21 +0100 Subject: [PATCH 1/2] Add WHEN STALE FAIL/INLINE syntax --- .../antlr4/io/trino/grammar/sql/SqlBase.g4 | 10 ++- .../io/trino/grammar/sql/TestSqlKeywords.java | 3 + .../trino/sql/analyzer/StatementAnalyzer.java | 3 + .../trino/sql/rewrite/ShowQueriesRewrite.java | 1 + .../TestCreateMaterializedViewTask.java | 5 ++ .../main/java/io/trino/sql/SqlFormatter.java | 2 + .../java/io/trino/sql/parser/AstBuilder.java | 10 +++ .../sql/tree/CreateMaterializedView.java | 18 ++++- .../java/io/trino/sql/TestSqlFormatter.java | 22 ++++++ .../io/trino/sql/parser/TestSqlParser.java | 78 +++++++++++++++++++ 10 files changed, 148 insertions(+), 4 deletions(-) diff --git a/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 b/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 index a6f8fd9951b8..02b83dbdb7fe 100644 --- a/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 +++ b/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 @@ -106,6 +106,7 @@ statement | CREATE (OR REPLACE)? MATERIALIZED VIEW (IF NOT EXISTS)? qualifiedName (GRACE PERIOD interval)? + (WHEN STALE (INLINE | FAIL))? (COMMENT string)? (WITH properties)? AS rootQuery #createMaterializedView | CREATE (OR REPLACE)? VIEW qualifiedName @@ -1035,10 +1036,10 @@ nonReserved | CALL | CALLED | CASCADE | CATALOG | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | CONDITIONAL | COPARTITION | CORRESPONDING | COUNT | CURRENT | DATA | DATE | DAY | DECLARE | DEFAULT | DEFINE | DEFINER | DENY | DESC | DESCRIPTOR | DETERMINISTIC | DISTRIBUTED | DO | DOUBLE | ELSEIF | EMPTY | ENCODING | ERROR | EXCLUDING | EXECUTE | EXPLAIN - | FAST | FETCH | FILTER | FINAL | FIRST | FOLLOWING | FORMAT | FORWARD | FUNCTION | FUNCTIONS + | FAIL | FAST | FETCH | FILTER | FINAL | FIRST | FOLLOWING | FORMAT | FORWARD | FUNCTION | FUNCTIONS | GRACE | GRANT | GRANTED | GRANTS | GRAPHVIZ | GROUPS | HOUR - | IF | IGNORE | IMMEDIATE | INCLUDING | INITIAL | INPUT | INTERVAL | INVOKER | IO | ITERATE | ISOLATION + | IF | IGNORE | IMMEDIATE | INCLUDING | INITIAL | INLINE | INPUT | INTERVAL | INVOKER | IO | ITERATE | ISOLATION | JSON | KEEP | KEY | KEYS | LANGUAGE | LAST | LATERAL | LEADING | LEAVE | LEVEL | LIMIT | LOCAL | LOGICAL | LOOP @@ -1049,7 +1050,7 @@ nonReserved | QUOTES | RANGE | READ | REFRESH | RENAME | REPEAT | REPEATABLE | REPLACE | RESET | RESPECT | RESTRICT | RETURN | RETURNING | RETURNS | REVOKE | ROLE | ROLES | ROLLBACK | ROW | ROWS | RUNNING | SCALAR | SCHEMA | SCHEMAS | SECOND | SECURITY | SEEK | SERIALIZABLE | SESSION | SET | SETS - | SHOW | SOME | START | STATS | SUBSET | SUBSTRING | SYSTEM + | SHOW | SOME | STALE | START | STATS | SUBSET | SUBSTRING | SYSTEM | TABLES | TABLESAMPLE | TEXT | TEXT_STRING | TIES | TIME | TIMESTAMP | TO | TRAILING | TRANSACTION | TRUNCATE | TRY_CAST | TYPE | UNBOUNDED | UNCOMMITTED | UNCONDITIONAL | UNIQUE | UNKNOWN | UNMATCHED | UNTIL | UPDATE | USE | USER | UTF16 | UTF32 | UTF8 | VALIDATE | VALUE | VERBOSE | VERSION | VIEW @@ -1141,6 +1142,7 @@ EXECUTE: 'EXECUTE'; EXISTS: 'EXISTS'; EXPLAIN: 'EXPLAIN'; EXTRACT: 'EXTRACT'; +FAIL: 'FAIL'; FALSE: 'FALSE'; FAST: 'FAST'; FETCH: 'FETCH'; @@ -1171,6 +1173,7 @@ IMMEDIATE: 'IMMEDIATE'; IN: 'IN'; INCLUDING: 'INCLUDING'; INITIAL: 'INITIAL'; +INLINE: 'INLINE'; INNER: 'INNER'; INPUT: 'INPUT'; INSERT: 'INSERT'; @@ -1301,6 +1304,7 @@ SET: 'SET'; SETS: 'SETS'; SHOW: 'SHOW'; SOME: 'SOME'; +STALE: 'STALE'; START: 'START'; STATS: 'STATS'; SUBSET: 'SUBSET'; diff --git a/core/trino-grammar/src/test/java/io/trino/grammar/sql/TestSqlKeywords.java b/core/trino-grammar/src/test/java/io/trino/grammar/sql/TestSqlKeywords.java index 0431ff9b28bf..cb44176dc6ef 100644 --- a/core/trino-grammar/src/test/java/io/trino/grammar/sql/TestSqlKeywords.java +++ b/core/trino-grammar/src/test/java/io/trino/grammar/sql/TestSqlKeywords.java @@ -109,6 +109,7 @@ public void test() "EXISTS", "EXPLAIN", "EXTRACT", + "FAIL", "FALSE", "FAST", "FETCH", @@ -139,6 +140,7 @@ public void test() "IN", "INCLUDING", "INITIAL", + "INLINE", "INNER", "INPUT", "INSERT", @@ -270,6 +272,7 @@ public void test() "SHOW", "SKIP", "SOME", + "STALE", "START", "STATS", "STRING", diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java index 858a828bb12a..9906babc4da8 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java @@ -1479,6 +1479,9 @@ protected Scope visitCreateMaterializedView(CreateMaterializedView node, Optiona throw semanticException(NOT_SUPPORTED, node, "'CREATE OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together"); } node.getGracePeriod().ifPresent(gracePeriod -> analyzeExpression(gracePeriod, Scope.create())); + if (node.getWhenStaleBehavior().isPresent()) { + throw new TrinoException(NOT_SUPPORTED, "WHEN STALE is not supported yet"); + } // analyze the query that creates the view StatementAnalyzer analyzer = statementAnalyzerFactory.createStatementAnalyzer(analysis, session, warningCollector, CorrelationSupport.ALLOWED); diff --git a/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java b/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java index 51d0cb1eb10e..fba03ce4215c 100644 --- a/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java +++ b/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java @@ -562,6 +562,7 @@ private Query showCreateMaterializedView(ShowCreate node) false, false, Optional.empty(), // TODO support GRACE PERIOD + Optional.empty(), // TODO support WHEN STALE propertyNodes, viewDefinition.get().getComment())).trim(); return singleValueQuery("Create Materialized View", sql); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java index 8168b2e34b2c..0074a841d3ac 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCreateMaterializedViewTask.java @@ -127,6 +127,7 @@ void testCreateMaterializedViewIfNotExists() false, true, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.empty()); @@ -147,6 +148,7 @@ void testCreateMaterializedViewWithExistingView() false, false, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.empty()); @@ -169,6 +171,7 @@ void testCreateMaterializedViewWithInvalidProperty() false, true, Optional.empty(), + Optional.empty(), ImmutableList.of(new Property(new NodeLocation(1, 88), new Identifier("baz"), new StringLiteral("abc"))), Optional.empty()); @@ -191,6 +194,7 @@ void testCreateMaterializedViewWithDefaultProperties() false, true, Optional.empty(), + Optional.empty(), ImmutableList.of( new Property(new Identifier("foo")), // set foo to DEFAULT new Property(new Identifier("bar"))), // set bar to DEFAULT @@ -217,6 +221,7 @@ public void testCreateDenyPermission() false, true, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.empty()); queryRunner.getAccessControl().deny(privilege("test_mv_deny", CREATE_MATERIALIZED_VIEW)); diff --git a/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java b/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java index 4185d540f760..f65fb618c253 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java @@ -1262,6 +1262,8 @@ protected Void visitCreateMaterializedView(CreateMaterializedView node, Integer builder.append(formatName(node.getName())); node.getGracePeriod().ifPresent(interval -> builder.append("\nGRACE PERIOD ").append(formatExpression(interval))); + node.getWhenStaleBehavior().ifPresent(whenStale -> + builder.append("\nWHEN STALE ").append(whenStale.name())); node.getComment().ifPresent(comment -> builder .append("\nCOMMENT ") .append(formatStringLiteral(comment))); diff --git a/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java b/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java index b8b2a76198a4..8a33fe5e4916 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java +++ b/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java @@ -57,6 +57,7 @@ import io.trino.sql.tree.CreateCatalog; import io.trino.sql.tree.CreateFunction; import io.trino.sql.tree.CreateMaterializedView; +import io.trino.sql.tree.CreateMaterializedView.WhenStaleBehavior; import io.trino.sql.tree.CreateRole; import io.trino.sql.tree.CreateSchema; import io.trino.sql.tree.CreateTable; @@ -613,6 +614,14 @@ public Node visitCreateMaterializedView(SqlBaseParser.CreateMaterializedViewCont gracePeriod = Optional.of((IntervalLiteral) visit(context.interval())); } + Optional whenStale = Optional.empty(); + if (context.INLINE() != null) { + whenStale = Optional.of(WhenStaleBehavior.INLINE); + } + else if (context.FAIL() != null) { + whenStale = Optional.of(WhenStaleBehavior.FAIL); + } + Optional comment = Optional.empty(); if (context.COMMENT() != null) { comment = Optional.of(visitString(context.string()).getValue()); @@ -630,6 +639,7 @@ public Node visitCreateMaterializedView(SqlBaseParser.CreateMaterializedViewCont context.REPLACE() != null, context.EXISTS() != null, gracePeriod, + whenStale, properties, comment); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CreateMaterializedView.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CreateMaterializedView.java index 2c131614d8f0..023670abd431 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/CreateMaterializedView.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CreateMaterializedView.java @@ -25,11 +25,18 @@ public class CreateMaterializedView extends Statement { + public enum WhenStaleBehavior + { + INLINE, + FAIL, + } + private final QualifiedName name; private final Query query; private final boolean replace; private final boolean notExists; private final Optional gracePeriod; + private final Optional whenStaleBehavior; private final List properties; private final Optional comment; @@ -40,6 +47,7 @@ public CreateMaterializedView( boolean replace, boolean notExists, Optional gracePeriod, + Optional whenStaleBehavior, List properties, Optional comment) { @@ -49,6 +57,7 @@ public CreateMaterializedView( this.replace = replace; this.notExists = notExists; this.gracePeriod = requireNonNull(gracePeriod, "gracePeriod is null"); + this.whenStaleBehavior = requireNonNull(whenStaleBehavior, "whenStaleBehavior is null"); this.properties = ImmutableList.copyOf(requireNonNull(properties, "properties is null")); this.comment = requireNonNull(comment, "comment is null"); } @@ -78,6 +87,11 @@ public Optional getGracePeriod() return gracePeriod; } + public Optional getWhenStaleBehavior() + { + return whenStaleBehavior; + } + public List getProperties() { return properties; @@ -106,7 +120,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(name, query, replace, notExists, gracePeriod, properties, comment); + return Objects.hash(name, query, replace, notExists, gracePeriod, whenStaleBehavior, properties, comment); } @Override @@ -124,6 +138,7 @@ public boolean equals(Object obj) && replace == o.replace && notExists == o.notExists && Objects.equals(gracePeriod, o.gracePeriod) + && Objects.equals(whenStaleBehavior, o.whenStaleBehavior) && Objects.equals(properties, o.properties) && Objects.equals(comment, o.comment); } @@ -137,6 +152,7 @@ public String toString() .add("replace", replace) .add("notExists", notExists) .add("gracePeriod", gracePeriod) + .add("whenStaleBehavior", whenStaleBehavior) .add("properties", properties) .add("comment", comment) .toString(); diff --git a/core/trino-parser/src/test/java/io/trino/sql/TestSqlFormatter.java b/core/trino-parser/src/test/java/io/trino/sql/TestSqlFormatter.java index 1b21adfb4a4e..4a317e6d5977 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/TestSqlFormatter.java +++ b/core/trino-parser/src/test/java/io/trino/sql/TestSqlFormatter.java @@ -23,6 +23,7 @@ import io.trino.sql.tree.CreateBranch; import io.trino.sql.tree.CreateCatalog; import io.trino.sql.tree.CreateMaterializedView; +import io.trino.sql.tree.CreateMaterializedView.WhenStaleBehavior; import io.trino.sql.tree.CreateTable; import io.trino.sql.tree.CreateTableAsSelect; import io.trino.sql.tree.CreateView; @@ -448,6 +449,7 @@ public void testCreateMaterializedView() false, false, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.empty()))) .isEqualTo("CREATE MATERIALIZED VIEW test_mv AS\n" + @@ -462,6 +464,7 @@ public void testCreateMaterializedView() false, false, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.of("攻殻機動隊")))) .isEqualTo("CREATE MATERIALIZED VIEW test_mv\n" + @@ -469,6 +472,25 @@ public void testCreateMaterializedView() "SELECT *\n" + "FROM\n" + " test_base\n"); + assertThat(formatSql( + new CreateMaterializedView( + new NodeLocation(1, 1), + QualifiedName.of("test_mv"), + simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("test_base"))), + false, + false, + Optional.empty(), + Optional.of(WhenStaleBehavior.FAIL), + ImmutableList.of(), + Optional.empty()))) + .isEqualTo( + """ + CREATE MATERIALIZED VIEW test_mv + WHEN STALE FAIL AS + SELECT * + FROM + test_base + """); } @Test diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java index a1ab2a78db87..8f9f0711e720 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java @@ -43,6 +43,7 @@ import io.trino.sql.tree.CreateBranch; import io.trino.sql.tree.CreateCatalog; import io.trino.sql.tree.CreateMaterializedView; +import io.trino.sql.tree.CreateMaterializedView.WhenStaleBehavior; import io.trino.sql.tree.CreateRole; import io.trino.sql.tree.CreateSchema; import io.trino.sql.tree.CreateTable; @@ -5893,6 +5894,7 @@ public void testCreateMaterializedView() false, false, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.empty())); @@ -5935,6 +5937,7 @@ public void testCreateMaterializedView() true, false, Optional.empty(), + Optional.empty(), ImmutableList.of(), Optional.of("A simple materialized view"))); @@ -5970,6 +5973,79 @@ public void testCreateMaterializedView() false, false, Optional.of(new IntervalLiteral(new NodeLocation(1, 41), "2", Sign.POSITIVE, IntervalField.DAY, Optional.empty())), + Optional.empty(), + ImmutableList.of(), + Optional.empty())); + + // WHEN STALE FAIL + assertThat(statement("CREATE MATERIALIZED VIEW a WHEN STALE FAIL AS SELECT * FROM t")) + .isEqualTo(new CreateMaterializedView( + new NodeLocation(1, 1), + QualifiedName.of(ImmutableList.of(new Identifier(new NodeLocation(1, 26), "a", false))), + new Query( + new NodeLocation(1, 47), + ImmutableList.of(), + ImmutableList.of(), + Optional.empty(), + new QuerySpecification( + new NodeLocation(1, 47), + new Select( + new NodeLocation(1, 47), + false, + ImmutableList.of(new AllColumns(new NodeLocation(1, 54), Optional.empty(), ImmutableList.of()))), + Optional.of(new Table( + new NodeLocation(1, 61), + QualifiedName.of(ImmutableList.of(new Identifier(new NodeLocation(1, 61), "t", false))))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableList.of(), + Optional.empty(), + Optional.empty(), + Optional.empty()), + Optional.empty(), + Optional.empty(), + Optional.empty()), + false, + false, + Optional.empty(), + Optional.of(WhenStaleBehavior.FAIL), + ImmutableList.of(), + Optional.empty())); + + // WHEN STALE INLINE + assertThat(statement("CREATE MATERIALIZED VIEW a WHEN STALE INLINE AS SELECT * FROM t")) + .isEqualTo(new CreateMaterializedView( + new NodeLocation(1, 1), + QualifiedName.of(ImmutableList.of(new Identifier(new NodeLocation(1, 26), "a", false))), + new Query( + new NodeLocation(1, 49), + ImmutableList.of(), + ImmutableList.of(), + Optional.empty(), + new QuerySpecification( + new NodeLocation(1, 49), + new Select( + new NodeLocation(1, 49), + false, + ImmutableList.of(new AllColumns(new NodeLocation(1, 56), Optional.empty(), ImmutableList.of()))), + Optional.of(new Table( + new NodeLocation(1, 63), + QualifiedName.of(ImmutableList.of(new Identifier(new NodeLocation(1, 63), "t", false))))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableList.of(), + Optional.empty(), + Optional.empty(), + Optional.empty()), + Optional.empty(), + Optional.empty(), + Optional.empty()), + false, + false, + Optional.empty(), + Optional.of(WhenStaleBehavior.INLINE), ImmutableList.of(), Optional.empty())); @@ -6016,6 +6092,7 @@ public void testCreateMaterializedView() true, false, Optional.empty(), + Optional.empty(), ImmutableList.of(new Property( new NodeLocation(2, 7), new Identifier(new NodeLocation(2, 7), "partitioned_by", false), @@ -6112,6 +6189,7 @@ AS WITH a (t, u) AS (SELECT * FROM x), b AS (SELECT * FROM a) TABLE b true, false, Optional.empty(), + Optional.empty(), ImmutableList.of(new Property( new NodeLocation(2, 7), new Identifier(new NodeLocation(2, 7), "partitioned_by", false), From 976cbc3008a5096e145d412a55583d078a52fa7c Mon Sep 17 00:00:00 2001 From: Piotr Rzysko Date: Tue, 18 Nov 2025 07:49:22 +0100 Subject: [PATCH 2/2] Add when stale behavior in ConnectorMaterializedViewDefinition This is a preparatory change that allows connectors to store information about the WHEN STALE behavior. The option cannot yet be used in practice, as execution will still fail with a NOT_SUPPORTED error. --- .../execution/CreateMaterializedViewTask.java | 11 +++++++++++ .../metadata/MaterializedViewDefinition.java | 11 +++++++++++ .../io/trino/metadata/MetadataManager.java | 1 + .../trino/sql/analyzer/StatementAnalyzer.java | 3 --- .../execution/BaseDataDefinitionTaskTest.java | 2 ++ .../io/trino/sql/analyzer/TestAnalyzer.java | 6 ++++++ .../sql/planner/TestMaterializedViews.java | 4 ++++ .../io/trino/sql/query/TestColumnMask.java | 3 +++ .../io/trino/testing/TestTestingMetadata.java | 1 + core/trino-spi/pom.xml | 5 +++++ .../spi/connector/ConnectorCapabilities.java | 1 + .../ConnectorMaterializedViewDefinition.java | 18 +++++++++++++++++- .../iceberg/catalog/AbstractTrinoCatalog.java | 1 + .../catalog/glue/TrinoGlueCatalog.java | 1 + .../iceberg/catalog/hms/TrinoHiveCatalog.java | 1 + .../iceberg/BaseIcebergConnectorTest.java | 1 + .../iceberg/catalog/BaseTrinoCatalogTest.java | 1 + ...TestTrinoHiveCatalogWithFileMetastore.java | 1 + .../catalog/glue/TestTrinoGlueCatalog.java | 1 + ...TestTrinoHiveCatalogWithHiveMetastore.java | 1 + .../lakehouse/TestLakehouseConnectorTest.java | 1 + .../io/trino/testing/BaseConnectorTest.java | 19 +++++++++++++++++++ .../testing/TestingConnectorBehavior.java | 1 + .../execution/TestEventListenerBasic.java | 2 ++ .../io/trino/execution/TestGrantOnTable.java | 1 + .../TestRefreshMaterializedView.java | 1 + .../io/trino/security/TestAccessControl.java | 1 + .../io/trino/tests/TestMockConnector.java | 1 + 28 files changed, 97 insertions(+), 4 deletions(-) diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java index 7fe0452ee37c..5ea8ad61bde9 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java @@ -27,6 +27,7 @@ import io.trino.metadata.ViewColumn; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; +import io.trino.spi.connector.ConnectorMaterializedViewDefinition.WhenStaleBehavior; import io.trino.spi.type.Type; import io.trino.sql.PlannerContext; import io.trino.sql.analyzer.Analysis; @@ -55,6 +56,7 @@ import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; import static io.trino.spi.StandardErrorCode.TYPE_MISMATCH; import static io.trino.spi.connector.ConnectorCapabilities.MATERIALIZED_VIEW_GRACE_PERIOD; +import static io.trino.spi.connector.ConnectorCapabilities.MATERIALIZED_VIEW_WHEN_STALE_BEHAVIOR; import static io.trino.sql.SqlFormatterUtil.getFormattedSql; import static io.trino.sql.analyzer.ConstantEvaluator.evaluateConstant; import static io.trino.sql.analyzer.SemanticExceptions.semanticException; @@ -156,12 +158,21 @@ Analysis executeInternal( return Duration.ofMillis(milliseconds); }); + Optional whenStale = statement.getWhenStaleBehavior() + .map(_ -> { + if (!plannerContext.getMetadata().getConnectorCapabilities(session, catalogHandle).contains(MATERIALIZED_VIEW_WHEN_STALE_BEHAVIOR)) { + throw semanticException(NOT_SUPPORTED, statement, "Catalog '%s' does not support WHEN STALE", catalogName); + } + throw semanticException(NOT_SUPPORTED, statement, "WHEN STALE is not supported yet"); + }); + MaterializedViewDefinition definition = new MaterializedViewDefinition( sql, session.getCatalog(), session.getSchema(), columns, gracePeriod, + whenStale, statement.getComment(), session.getIdentity(), session.getPath().getPath().stream() diff --git a/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java b/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java index 7e0f867ffa9a..03670d7f8bc7 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java +++ b/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java @@ -16,6 +16,7 @@ import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; import io.trino.spi.connector.ConnectorMaterializedViewDefinition; +import io.trino.spi.connector.ConnectorMaterializedViewDefinition.WhenStaleBehavior; import io.trino.spi.security.Identity; import java.time.Duration; @@ -31,6 +32,7 @@ public class MaterializedViewDefinition extends ViewDefinition { private final Optional gracePeriod; + private final Optional whenStaleBehavior; private final Optional storageTable; public MaterializedViewDefinition( @@ -39,6 +41,7 @@ public MaterializedViewDefinition( Optional schema, List columns, Optional gracePeriod, + Optional whenStaleBehavior, Optional comment, Identity owner, List path, @@ -47,6 +50,7 @@ public MaterializedViewDefinition( super(originalSql, catalog, schema, columns, comment, Optional.of(owner), path); this.gracePeriod = requireNonNull(gracePeriod, "gracePeriod is null"); checkArgument(gracePeriod.isEmpty() || !gracePeriod.get().isNegative(), "gracePeriod cannot be negative: %s", gracePeriod); + this.whenStaleBehavior = requireNonNull(whenStaleBehavior, "whenStaleBehavior is null"); this.storageTable = requireNonNull(storageTable, "storageTable is null"); } @@ -55,6 +59,11 @@ public Optional getGracePeriod() return gracePeriod; } + public Optional getWhenStaleBehavior() + { + return whenStaleBehavior; + } + public Optional getStorageTable() { return storageTable; @@ -71,6 +80,7 @@ public ConnectorMaterializedViewDefinition toConnectorMaterializedViewDefinition .map(column -> new ConnectorMaterializedViewDefinition.Column(column.name(), column.type(), column.comment())) .collect(toImmutableList()), getGracePeriod(), + whenStaleBehavior, getComment(), getRunAsIdentity().map(Identity::getUser), getPath()); @@ -85,6 +95,7 @@ public String toString() .add("schema", getSchema().orElse(null)) .add("columns", getColumns()) .add("gracePeriod", gracePeriod.orElse(null)) + .add("whenStaleBehavior", whenStaleBehavior.orElse(null)) .add("comment", getComment().orElse(null)) .add("runAsIdentity", getRunAsIdentity()) .add("path", getPath()) diff --git a/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java b/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java index a3c272ec9c6c..d71098152ce3 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java +++ b/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java @@ -1827,6 +1827,7 @@ private static MaterializedViewDefinition createMaterializedViewDefinition(Conne .map(column -> new ViewColumn(column.getName(), column.getType(), Optional.empty())) .collect(toImmutableList()), view.getGracePeriod(), + view.getWhenStaleBehavior(), view.getComment(), runAsIdentity, view.getPath(), diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java index 9906babc4da8..858a828bb12a 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java @@ -1479,9 +1479,6 @@ protected Scope visitCreateMaterializedView(CreateMaterializedView node, Optiona throw semanticException(NOT_SUPPORTED, node, "'CREATE OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together"); } node.getGracePeriod().ifPresent(gracePeriod -> analyzeExpression(gracePeriod, Scope.create())); - if (node.getWhenStaleBehavior().isPresent()) { - throw new TrinoException(NOT_SUPPORTED, "WHEN STALE is not supported yet"); - } // analyze the query that creates the view StatementAnalyzer analyzer = statementAnalyzerFactory.createStatementAnalyzer(analysis, session, warningCollector, CorrelationSupport.ALLOWED); diff --git a/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java b/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java index ecc1b678e8c0..752ce28fda1b 100644 --- a/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java +++ b/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java @@ -205,6 +205,7 @@ protected MaterializedViewDefinition someMaterializedView(String sql, List columnName.equals(currentViewColumn.name()) ? new ViewColumn(currentViewColumn.name(), currentViewColumn.type(), comment) : currentViewColumn) .collect(toImmutableList()), view.getGracePeriod(), + view.getWhenStaleBehavior(), view.getComment(), view.getRunAsIdentity().get(), view.getPath(), diff --git a/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java b/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java index ff6e6d9a85fb..f043bcb366e0 100644 --- a/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java +++ b/core/trino-main/src/test/java/io/trino/sql/analyzer/TestAnalyzer.java @@ -7889,6 +7889,7 @@ public void setup() Optional.of("s1"), ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), + Optional.empty(), Optional.of("comment"), Identity.ofUser("user"), ImmutableList.of(), @@ -8019,6 +8020,7 @@ public void setup() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t1"))), @@ -8073,6 +8075,7 @@ public void setup() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), // t3 has a, b column and hidden column x @@ -8093,6 +8096,7 @@ public void setup() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t2"))), @@ -8112,6 +8116,7 @@ public void setup() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("c", BIGINT.getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t2"))), @@ -8131,6 +8136,7 @@ public void setup() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", RowType.anonymousRow(TINYINT).getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TPCH_CATALOG, "s1", "t2"))), diff --git a/core/trino-main/src/test/java/io/trino/sql/planner/TestMaterializedViews.java b/core/trino-main/src/test/java/io/trino/sql/planner/TestMaterializedViews.java index 224d5a4ed777..0140c1a6cc4c 100644 --- a/core/trino-main/src/test/java/io/trino/sql/planner/TestMaterializedViews.java +++ b/core/trino-main/src/test/java/io/trino/sql/planner/TestMaterializedViews.java @@ -186,6 +186,7 @@ protected PlanTester createPlanTester() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), Optional.of(STALE_MV_STALENESS.plusHours(1)), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TEST_CATALOG_NAME, SCHEMA, "storage_table"))); @@ -220,6 +221,7 @@ protected PlanTester createPlanTester() ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TEST_CATALOG_NAME, SCHEMA, "storage_table_with_casts"))); @@ -254,6 +256,7 @@ protected PlanTester createPlanTester() ImmutableList.of(new ViewColumn("id", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("ts", timestampWithTimezone3.getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TEST_CATALOG_NAME, SCHEMA, "timestamp_test_storage"))); @@ -285,6 +288,7 @@ private void createMaterializedView(String materializedViewName, String query) ImmutableList.of(new ViewColumn("a", BIGINT.getTypeId(), Optional.empty()), new ViewColumn("b", BIGINT.getTypeId(), Optional.empty())), Optional.of(STALE_MV_STALENESS.plusHours(1)), Optional.empty(), + Optional.empty(), Identity.ofUser("some user"), ImmutableList.of(), Optional.of(new CatalogSchemaTableName(TEST_CATALOG_NAME, SCHEMA, "storage_table"))); diff --git a/core/trino-main/src/test/java/io/trino/sql/query/TestColumnMask.java b/core/trino-main/src/test/java/io/trino/sql/query/TestColumnMask.java index 560faad79a9f..a9e0ecb7da78 100644 --- a/core/trino-main/src/test/java/io/trino/sql/query/TestColumnMask.java +++ b/core/trino-main/src/test/java/io/trino/sql/query/TestColumnMask.java @@ -133,6 +133,7 @@ public TestColumnMask() new ConnectorMaterializedViewDefinition.Column("comment", VarcharType.createVarcharType(152).getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of(VIEW_OWNER), ImmutableList.of()); @@ -148,6 +149,7 @@ public TestColumnMask() new ConnectorMaterializedViewDefinition.Column("comment", VarcharType.createVarcharType(152).getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of(VIEW_OWNER), ImmutableList.of()); @@ -163,6 +165,7 @@ public TestColumnMask() new ConnectorMaterializedViewDefinition.Column("comment", VarcharType.createVarcharType(152).getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of(VIEW_OWNER), ImmutableList.of()); diff --git a/core/trino-main/src/test/java/io/trino/testing/TestTestingMetadata.java b/core/trino-main/src/test/java/io/trino/testing/TestTestingMetadata.java index d188741497da..26d3ebca70e7 100644 --- a/core/trino-main/src/test/java/io/trino/testing/TestTestingMetadata.java +++ b/core/trino-main/src/test/java/io/trino/testing/TestTestingMetadata.java @@ -61,6 +61,7 @@ private static ConnectorMaterializedViewDefinition someMaterializedView() ImmutableList.of(new Column("test", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of("owner"), ImmutableList.of()); } diff --git a/core/trino-spi/pom.xml b/core/trino-spi/pom.xml index 14ab18f30ce2..d7e7ad4eecaf 100644 --- a/core/trino-spi/pom.xml +++ b/core/trino-spi/pom.xml @@ -297,6 +297,11 @@ method void io.trino.spi.block.PageBuilderStatus::<init>() Method is unnecessary + + java.method.numberOfParametersChanged + method void io.trino.spi.connector.ConnectorMaterializedViewDefinition::<init>(java.lang.String, java.util.Optional<io.trino.spi.connector.CatalogSchemaTableName>, java.util.Optional<java.lang.String>, java.util.Optional<java.lang.String>, java.util.List<io.trino.spi.connector.ConnectorMaterializedViewDefinition.Column>, java.util.Optional<java.time.Duration>, java.util.Optional<java.lang.String>, java.util.Optional<java.lang.String>, java.util.List<io.trino.spi.connector.CatalogSchemaName>) + method void io.trino.spi.connector.ConnectorMaterializedViewDefinition::<init>(java.lang.String, java.util.Optional<io.trino.spi.connector.CatalogSchemaTableName>, java.util.Optional<java.lang.String>, java.util.Optional<java.lang.String>, java.util.List<io.trino.spi.connector.ConnectorMaterializedViewDefinition.Column>, java.util.Optional<java.time.Duration>, java.util.Optional<io.trino.spi.connector.ConnectorMaterializedViewDefinition.WhenStaleBehavior>, java.util.Optional<java.lang.String>, java.util.Optional<java.lang.String>, java.util.List<io.trino.spi.connector.CatalogSchemaName>) + diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorCapabilities.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorCapabilities.java index faec15da9b0b..5e50af6ec33a 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorCapabilities.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorCapabilities.java @@ -18,4 +18,5 @@ public enum ConnectorCapabilities DEFAULT_COLUMN_VALUE, NOT_NULL_COLUMN_CONSTRAINT, MATERIALIZED_VIEW_GRACE_PERIOD, + MATERIALIZED_VIEW_WHEN_STALE_BEHAVIOR, } diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMaterializedViewDefinition.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMaterializedViewDefinition.java index d7ebd0097f49..559a48e12d5a 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMaterializedViewDefinition.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMaterializedViewDefinition.java @@ -27,12 +27,19 @@ public class ConnectorMaterializedViewDefinition { + public enum WhenStaleBehavior + { + INLINE, + FAIL, + } + private final String originalSql; private final Optional storageTable; private final Optional catalog; private final Optional schema; private final List columns; private final Optional gracePeriod; + private final Optional whenStaleBehavior; private final Optional comment; private final Optional owner; private final List path; @@ -44,6 +51,7 @@ public ConnectorMaterializedViewDefinition( Optional schema, List columns, Optional gracePeriod, + Optional whenStaleBehavior, Optional comment, Optional owner, List path) @@ -55,6 +63,7 @@ public ConnectorMaterializedViewDefinition( this.columns = List.copyOf(requireNonNull(columns, "columns is null")); checkArgument(gracePeriod.isEmpty() || !gracePeriod.get().isNegative(), "gracePeriod cannot be negative: %s", gracePeriod); this.gracePeriod = gracePeriod; + this.whenStaleBehavior = requireNonNull(whenStaleBehavior, "whenStaleBehavior is null"); this.comment = requireNonNull(comment, "comment is null"); this.owner = requireNonNull(owner, "owner is null"); this.path = List.copyOf(path); @@ -97,6 +106,11 @@ public Optional getGracePeriod() return gracePeriod; } + public Optional getWhenStaleBehavior() + { + return whenStaleBehavior; + } + public Optional getComment() { return comment; @@ -122,6 +136,7 @@ public String toString() schema.ifPresent(value -> joiner.add("schema=" + value)); joiner.add("columns=" + columns); gracePeriod.ifPresent(value -> joiner.add("gracePeriod=" + gracePeriod)); + whenStaleBehavior.ifPresent(value -> joiner.add("whenStaleBehavior=" + value.name())); comment.ifPresent(value -> joiner.add("comment=" + value)); joiner.add("owner=" + owner); joiner.add(path.stream().map(CatalogSchemaName::toString).collect(joining(", ", "path=(", ")"))); @@ -144,6 +159,7 @@ public boolean equals(Object o) Objects.equals(schema, that.schema) && Objects.equals(columns, that.columns) && Objects.equals(gracePeriod, that.gracePeriod) && + Objects.equals(whenStaleBehavior, that.whenStaleBehavior) && Objects.equals(comment, that.comment) && Objects.equals(owner, that.owner) && Objects.equals(path, that.path); @@ -152,7 +168,7 @@ public boolean equals(Object o) @Override public int hashCode() { - return Objects.hash(originalSql, storageTable, catalog, schema, columns, gracePeriod, comment, owner, path); + return Objects.hash(originalSql, storageTable, catalog, schema, columns, gracePeriod, whenStaleBehavior, comment, owner, path); } public static final class Column diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/AbstractTrinoCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/AbstractTrinoCatalog.java index 4268acc934d5..6bbbd92bb2fa 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/AbstractTrinoCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/AbstractTrinoCatalog.java @@ -466,6 +466,7 @@ protected ConnectorMaterializedViewDefinition getMaterializedViewDefinition( definition.schema(), toSpiMaterializedViewColumns(definition.columns()), definition.gracePeriod(), + Optional.empty(), definition.comment(), owner, definition.path()); diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/glue/TrinoGlueCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/glue/TrinoGlueCatalog.java index 85d761d1062d..f6045e072616 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/glue/TrinoGlueCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/glue/TrinoGlueCatalog.java @@ -1232,6 +1232,7 @@ public void updateMaterializedViewColumnComment(ConnectorSession session, Schema .map(currentViewColumn -> Objects.equals(columnName, currentViewColumn.getName()) ? new ConnectorMaterializedViewDefinition.Column(currentViewColumn.getName(), currentViewColumn.getType(), comment) : currentViewColumn) .collect(toImmutableList()), definition.getGracePeriod(), + definition.getWhenStaleBehavior(), definition.getComment(), definition.getOwner(), definition.getPath()); diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/hms/TrinoHiveCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/hms/TrinoHiveCatalog.java index 06460d638b7e..34f9d0d32cf0 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/hms/TrinoHiveCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/hms/TrinoHiveCatalog.java @@ -694,6 +694,7 @@ public void updateMaterializedViewColumnComment(ConnectorSession session, Schema : currentViewColumn) .collect(toImmutableList()), definition.getGracePeriod(), + definition.getWhenStaleBehavior(), definition.getComment(), definition.getOwner(), definition.getPath()); diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java index 608bdf00f2a0..0e4768f4fbdd 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java @@ -280,6 +280,7 @@ protected boolean hasBehavior(TestingConnectorBehavior connectorBehavior) SUPPORTS_LIMIT_PUSHDOWN, SUPPORTS_REFRESH_VIEW, SUPPORTS_RENAME_MATERIALIZED_VIEW_ACROSS_SCHEMAS, + SUPPORTS_CREATE_MATERIALIZED_VIEW_WHEN_STALE, SUPPORTS_TOPN_PUSHDOWN -> false; default -> super.hasBehavior(connectorBehavior); }; diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/BaseTrinoCatalogTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/BaseTrinoCatalogTest.java index 212d9c282390..6288a52f6b7a 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/BaseTrinoCatalogTest.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/BaseTrinoCatalogTest.java @@ -647,6 +647,7 @@ private static ConnectorMaterializedViewDefinition someMaterializedView() ImmutableList.of(new ConnectorMaterializedViewDefinition.Column("test", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of("owner"), ImmutableList.of()); } diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/file/TestTrinoHiveCatalogWithFileMetastore.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/file/TestTrinoHiveCatalogWithFileMetastore.java index d1147eed5fa8..9c9329608e7d 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/file/TestTrinoHiveCatalogWithFileMetastore.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/file/TestTrinoHiveCatalogWithFileMetastore.java @@ -153,6 +153,7 @@ private void testDropMaterializedView(boolean useUniqueTableLocations) Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), ImmutableList.of()), ImmutableMap.of(FILE_FORMAT_PROPERTY, PARQUET, FORMAT_VERSION_PROPERTY, 1), false, diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/glue/TestTrinoGlueCatalog.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/glue/TestTrinoGlueCatalog.java index 7cfacf22d940..b2c71e66174d 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/glue/TestTrinoGlueCatalog.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/glue/TestTrinoGlueCatalog.java @@ -186,6 +186,7 @@ public void testCreateMaterializedViewWithSystemSecurity() ImmutableList.of(new ConnectorMaterializedViewDefinition.Column("col1", INTEGER.getTypeId(), Optional.empty())), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.of("test_owner"), ImmutableList.of()), ImmutableMap.of(FILE_FORMAT_PROPERTY, PARQUET, FORMAT_VERSION_PROPERTY, 1), diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/hms/TestTrinoHiveCatalogWithHiveMetastore.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/hms/TestTrinoHiveCatalogWithHiveMetastore.java index 2baf83cd2aab..2974187cc08a 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/hms/TestTrinoHiveCatalogWithHiveMetastore.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/catalog/hms/TestTrinoHiveCatalogWithHiveMetastore.java @@ -215,6 +215,7 @@ public void testCreateMaterializedView() Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), ImmutableList.of()), ImmutableMap.of(FILE_FORMAT_PROPERTY, PARQUET, FORMAT_VERSION_PROPERTY, 1), false, diff --git a/plugin/trino-lakehouse/src/test/java/io/trino/plugin/lakehouse/TestLakehouseConnectorTest.java b/plugin/trino-lakehouse/src/test/java/io/trino/plugin/lakehouse/TestLakehouseConnectorTest.java index 465790ae611b..b77ec9d1a14f 100644 --- a/plugin/trino-lakehouse/src/test/java/io/trino/plugin/lakehouse/TestLakehouseConnectorTest.java +++ b/plugin/trino-lakehouse/src/test/java/io/trino/plugin/lakehouse/TestLakehouseConnectorTest.java @@ -127,6 +127,7 @@ protected boolean hasBehavior(TestingConnectorBehavior connectorBehavior) SUPPORTS_LIMIT_PUSHDOWN, SUPPORTS_REFRESH_VIEW, SUPPORTS_RENAME_MATERIALIZED_VIEW_ACROSS_SCHEMAS, + SUPPORTS_CREATE_MATERIALIZED_VIEW_WHEN_STALE, SUPPORTS_TOPN_PUSHDOWN -> false; default -> super.hasBehavior(connectorBehavior); }; 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 9ef397e19ee1..4651cf1490b4 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 @@ -127,6 +127,7 @@ import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_FUNCTION; import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_MATERIALIZED_VIEW; import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_MATERIALIZED_VIEW_GRACE_PERIOD; +import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_MATERIALIZED_VIEW_WHEN_STALE; import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_OR_REPLACE_TABLE; import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_SCHEMA; import static io.trino.testing.TestingConnectorBehavior.SUPPORTS_CREATE_TABLE; @@ -1596,6 +1597,24 @@ public void testMaterializedViewGracePeriod() } } + @Test + public void testMaterializedViewWhenStale() + { + skipTestUnless(hasBehavior(SUPPORTS_CREATE_MATERIALIZED_VIEW)); + + String catalog = getSession().getCatalog().orElseThrow(); + String viewName = "test_mv_when_stale_" + randomNameSuffix(); + + if (!hasBehavior(SUPPORTS_CREATE_MATERIALIZED_VIEW_WHEN_STALE)) { + assertQueryFails( + "CREATE MATERIALIZED VIEW " + viewName + " WHEN STALE FAIL AS SELECT * FROM nation", + "line 1:1: Catalog '%s' does not support WHEN STALE".formatted(catalog)); + return; + } + + throw new UnsupportedOperationException("Not implemented"); + } + @Test public void testFederatedMaterializedView() { diff --git a/testing/trino-testing/src/main/java/io/trino/testing/TestingConnectorBehavior.java b/testing/trino-testing/src/main/java/io/trino/testing/TestingConnectorBehavior.java index aae3e3c4922d..ce14457e977e 100644 --- a/testing/trino-testing/src/main/java/io/trino/testing/TestingConnectorBehavior.java +++ b/testing/trino-testing/src/main/java/io/trino/testing/TestingConnectorBehavior.java @@ -108,6 +108,7 @@ public enum TestingConnectorBehavior SUPPORTS_CREATE_MATERIALIZED_VIEW, SUPPORTS_CREATE_MATERIALIZED_VIEW_GRACE_PERIOD(SUPPORTS_CREATE_MATERIALIZED_VIEW), + SUPPORTS_CREATE_MATERIALIZED_VIEW_WHEN_STALE(SUPPORTS_CREATE_MATERIALIZED_VIEW), SUPPORTS_CREATE_FEDERATED_MATERIALIZED_VIEW(SUPPORTS_CREATE_MATERIALIZED_VIEW), // i.e. an MV that spans catalogs SUPPORTS_MATERIALIZED_VIEW_FRESHNESS_FROM_BASE_TABLES(SUPPORTS_CREATE_MATERIALIZED_VIEW), SUPPORTS_RENAME_MATERIALIZED_VIEW(SUPPORTS_CREATE_MATERIALIZED_VIEW), diff --git a/testing/trino-tests/src/test/java/io/trino/execution/TestEventListenerBasic.java b/testing/trino-tests/src/test/java/io/trino/execution/TestEventListenerBasic.java index fad98bed11c7..944c36036b15 100644 --- a/testing/trino-tests/src/test/java/io/trino/execution/TestEventListenerBasic.java +++ b/testing/trino-tests/src/test/java/io/trino/execution/TestEventListenerBasic.java @@ -222,6 +222,7 @@ public Iterable getConnectorFactories() ImmutableList.of(new Column("test_column", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of("alice"), ImmutableList.of()); ConnectorMaterializedViewDefinition definitionFresh = new ConnectorMaterializedViewDefinition( @@ -235,6 +236,7 @@ public Iterable getConnectorFactories() .collect(toImmutableList()), Optional.of(Duration.ofDays(1)), Optional.empty(), + Optional.empty(), Optional.of("alice"), ImmutableList.of()); return ImmutableMap.of( diff --git a/testing/trino-tests/src/test/java/io/trino/execution/TestGrantOnTable.java b/testing/trino-tests/src/test/java/io/trino/execution/TestGrantOnTable.java index 9018ec5c61b9..297330cc6b46 100644 --- a/testing/trino-tests/src/test/java/io/trino/execution/TestGrantOnTable.java +++ b/testing/trino-tests/src/test/java/io/trino/execution/TestGrantOnTable.java @@ -99,6 +99,7 @@ materializedView, new ConnectorMaterializedViewDefinition( ImmutableList.of(new ConnectorMaterializedViewDefinition.Column("test_column", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of("alice"), ImmutableList.of()))) .build(); diff --git a/testing/trino-tests/src/test/java/io/trino/execution/TestRefreshMaterializedView.java b/testing/trino-tests/src/test/java/io/trino/execution/TestRefreshMaterializedView.java index 61f7911c4eed..45678358d844 100644 --- a/testing/trino-tests/src/test/java/io/trino/execution/TestRefreshMaterializedView.java +++ b/testing/trino-tests/src/test/java/io/trino/execution/TestRefreshMaterializedView.java @@ -104,6 +104,7 @@ protected QueryRunner createQueryRunner() ImmutableList.of(new ConnectorMaterializedViewDefinition.Column("nationkey", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of("alice"), ImmutableList.of()))) .withDelegateMaterializedViewRefreshToConnector((connectorSession, schemaTableName) -> true) diff --git a/testing/trino-tests/src/test/java/io/trino/security/TestAccessControl.java b/testing/trino-tests/src/test/java/io/trino/security/TestAccessControl.java index 8724a1672291..9ed632c53386 100644 --- a/testing/trino-tests/src/test/java/io/trino/security/TestAccessControl.java +++ b/testing/trino-tests/src/test/java/io/trino/security/TestAccessControl.java @@ -219,6 +219,7 @@ public Map apply(Connector Optional.empty(), ImmutableList.of(new ConnectorMaterializedViewDefinition.Column("test", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), + Optional.empty(), Optional.of("comment"), Optional.of("owner"), ImmutableList.of()); diff --git a/testing/trino-tests/src/test/java/io/trino/tests/TestMockConnector.java b/testing/trino-tests/src/test/java/io/trino/tests/TestMockConnector.java index 7dc57c38fa77..48693fe345ab 100644 --- a/testing/trino-tests/src/test/java/io/trino/tests/TestMockConnector.java +++ b/testing/trino-tests/src/test/java/io/trino/tests/TestMockConnector.java @@ -105,6 +105,7 @@ protected QueryRunner createQueryRunner() ImmutableList.of(new Column("nationkey", BIGINT.getTypeId(), Optional.empty())), Optional.of(Duration.ZERO), Optional.empty(), + Optional.empty(), Optional.of("alice"), ImmutableList.of()))) .withData(schemaTableName -> {