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 71e5ae3d74e1..e984b9d99bc4 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 @@ -109,7 +109,8 @@ statement (WHEN STALE (INLINE | FAIL))? (COMMENT string)? (WITH properties)? AS rootQuery #createMaterializedView - | CREATE (OR REPLACE)? VIEW qualifiedName + | CREATE (OR REPLACE)? VIEW + (IF NOT EXISTS)? qualifiedName (COMMENT string)? (SECURITY (DEFINER | INVOKER))? (WITH properties)? AS rootQuery #createView diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java index 2842d2cae0b7..7a856df34db9 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java @@ -25,6 +25,7 @@ import io.trino.metadata.ViewDefinition; import io.trino.metadata.ViewPropertyManager; import io.trino.security.AccessControl; +import io.trino.spi.connector.SaveMode; import io.trino.spi.security.Identity; import io.trino.sql.PlannerContext; import io.trino.sql.analyzer.Analysis; @@ -99,9 +100,12 @@ public ListenableFuture execute( throw semanticException(TABLE_ALREADY_EXISTS, statement, "Materialized view already exists: '%s'", name); } if (metadata.isView(session, name)) { - if (!statement.isReplace()) { + if (!statement.isReplace() && !statement.isNotExists()) { throw semanticException(TABLE_ALREADY_EXISTS, statement, "View already exists: '%s'", name); } + if (statement.isNotExists()) { + return immediateVoidFuture(); + } } else if (metadata.getTableHandle(session, name).isPresent()) { throw semanticException(TABLE_ALREADY_EXISTS, statement, "Table already exists: '%s'", name); @@ -148,11 +152,20 @@ else if (metadata.getTableHandle(session, name).isPresent()) { .filter(element -> !element.getCatalogName().equals(GlobalSystemConnector.NAME)) .collect(toImmutableList())); - metadata.createView(session, name, definition, properties, statement.isReplace()); + metadata.createView(session, name, definition, properties, toConnectorSaveMode(statement.getSaveMode())); stateMachine.setOutput(analysis.getTarget()); stateMachine.setReferencedTables(analysis.getReferencedTables()); return immediateVoidFuture(); } + + private static SaveMode toConnectorSaveMode(io.trino.sql.tree.SaveMode saveMode) + { + return switch (saveMode) { + case FAIL -> SaveMode.FAIL; + case IGNORE -> SaveMode.IGNORE; + case REPLACE -> SaveMode.REPLACE; + }; + } } diff --git a/core/trino-main/src/main/java/io/trino/metadata/Metadata.java b/core/trino-main/src/main/java/io/trino/metadata/Metadata.java index c572f9a94f4f..75fb7467b71f 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/Metadata.java +++ b/core/trino-main/src/main/java/io/trino/metadata/Metadata.java @@ -548,7 +548,7 @@ Optional finishRefreshMaterializedView( /** * Creates the specified view with the specified view definition. */ - void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map properties, boolean replace); + void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map properties, SaveMode saveMode); /** * Rename the specified view. 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 eb933d89301d..e4276b319980 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 @@ -1670,13 +1670,13 @@ private Optional getViewInternal(Session session, Quali } @Override - public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map viewProperties, boolean replace) + public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map viewProperties, SaveMode saveMode) { CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.catalogName()); CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(); ConnectorMetadata metadata = catalogMetadata.getMetadata(session); - metadata.createView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), definition.toConnectorViewDefinition(), viewProperties, replace); + metadata.createView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), definition.toConnectorViewDefinition(), viewProperties, saveMode); if (catalogMetadata.getSecurityManagement() == SYSTEM) { systemSecurityMetadata.tableCreated(session, viewName.asCatalogSchemaTableName()); } 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 697ec0cf5ac4..fec68cb296e0 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 @@ -1064,6 +1064,9 @@ protected Scope visitCreateView(CreateView node, Optional scope) { QualifiedObjectName viewName = createQualifiedObjectName(session, node, node.getName()); + if (node.isReplace() && node.isNotExists()) { + throw semanticException(NOT_SUPPORTED, node, "'CREATE OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together"); + } node.getQuery().getFunctions().stream().findFirst().ifPresent(function -> { throw semanticException(NOT_SUPPORTED, function, "Views cannot contain inline functions"); }); 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 a1a8b7fe1912..04cf56551656 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 @@ -83,6 +83,7 @@ import io.trino.sql.tree.QuerySpecification; import io.trino.sql.tree.Relation; import io.trino.sql.tree.Row; +import io.trino.sql.tree.SaveMode; import io.trino.sql.tree.SelectItem; import io.trino.sql.tree.ShowBranches; import io.trino.sql.tree.ShowCatalogs; @@ -614,7 +615,7 @@ private Query showCreateView(ShowCreate node) node.getLocation().orElseThrow(), QualifiedName.of(ImmutableList.of(catalogName, schemaName, tableName)), query, - false, + SaveMode.FAIL, viewDefinition.get().getComment(), Optional.of(security), propertyNodes)) diff --git a/core/trino-main/src/main/java/io/trino/testing/TestingMetadata.java b/core/trino-main/src/main/java/io/trino/testing/TestingMetadata.java index 0fd07a21a133..c583cb9ea411 100644 --- a/core/trino-main/src/main/java/io/trino/testing/TestingMetadata.java +++ b/core/trino-main/src/main/java/io/trino/testing/TestingMetadata.java @@ -205,10 +205,17 @@ public void dropTable(ConnectorSession session, ConnectorTableHandle tableHandle } @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + { + createView(session, viewName, definition, viewProperties, replace ? SaveMode.REPLACE : SaveMode.FAIL); + } + + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) { checkArgument(viewProperties.isEmpty(), "This connector does not support creating views with properties"); - if (replace) { + if (saveMode == REPLACE) { views.put(viewName, definition); } else if (views.putIfAbsent(viewName, definition) != null) { diff --git a/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java b/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java index 4ff7d28c5c81..4f27b7556550 100644 --- a/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java +++ b/core/trino-main/src/main/java/io/trino/tracing/TracingConnectorMetadata.java @@ -802,6 +802,7 @@ public void finishMerge(ConnectorSession session, ConnectorMergeTableHandle tabl } @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) { Span span = startSpan("createView", viewName); @@ -810,6 +811,15 @@ public void createView(ConnectorSession session, SchemaTableName viewName, Conne } } + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) + { + Span span = startSpan("createView", viewName); + try (var _ = scopedSpan(span)) { + delegate.createView(session, viewName, definition, viewProperties, saveMode); + } + } + @Override public void renameView(ConnectorSession session, SchemaTableName source, SchemaTableName target) { diff --git a/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java b/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java index 9b71a87914c2..e064ee24f303 100644 --- a/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java +++ b/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java @@ -992,11 +992,11 @@ public Optional getSchemaOwner(Session session, CatalogSchemaNam } @Override - public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map properties, boolean replace) + public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map properties, SaveMode saveMode) { Span span = startSpan("createView", viewName); try (var _ = scopedSpan(span)) { - delegate.createView(session, viewName, definition, properties, replace); + delegate.createView(session, viewName, definition, properties, saveMode); } } diff --git a/core/trino-main/src/test/java/io/trino/connector/MockConnector.java b/core/trino-main/src/test/java/io/trino/connector/MockConnector.java index fc907dafe152..bceb9dff14f1 100644 --- a/core/trino-main/src/test/java/io/trino/connector/MockConnector.java +++ b/core/trino-main/src/test/java/io/trino/connector/MockConnector.java @@ -711,8 +711,12 @@ public void renameField(ConnectorSession session, ConnectorTableHandle tableHand public void dropColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column) {} @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) {} + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) {} + @Override public void renameView(ConnectorSession session, SchemaTableName source, SchemaTableName target) {} 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 4b5c83591a05..22406a5d36fe 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 @@ -622,9 +622,9 @@ public boolean isView(Session session, QualifiedObjectName viewName) } @Override - public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map viewProperties, boolean replace) + public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map viewProperties, SaveMode saveMode) { - checkArgument(replace || !views.containsKey(viewName.asSchemaTableName())); + checkArgument(saveMode == REPLACE || !views.containsKey(viewName.asSchemaTableName())); views.put(viewName.asSchemaTableName(), definition); } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestAddColumnTask.java b/core/trino-main/src/test/java/io/trino/execution/TestAddColumnTask.java index 50d61bb66a56..2416fa794327 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestAddColumnTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestAddColumnTask.java @@ -410,7 +410,7 @@ public void testAddColumnAlreadyExist() public void testAddColumnOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeAddColumn(asQualifiedName(viewName), QualifiedName.of("test"), INTEGER, Optional.empty(), new ColumnPosition.Last(), false, false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCommentTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCommentTask.java index 488212d2d08a..a6c5c62d719f 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCommentTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCommentTask.java @@ -62,7 +62,7 @@ public void testCommentTable() public void testCommentTableOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(setComment(TABLE, asQualifiedName(viewName), Optional.of("new comment")))) .hasErrorCode(TABLE_NOT_FOUND) @@ -84,7 +84,7 @@ public void testCommentTableOnMaterializedView() public void testCommentView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertThat(metadata.isView(testSession, viewName)).isTrue(); getFutureValue(setComment(VIEW, asQualifiedName(viewName), Optional.of("new comment"))); @@ -133,7 +133,7 @@ public void testCommentViewColumn() QualifiedObjectName viewName = qualifiedObjectName("existing_view"); QualifiedName columnName = qualifiedColumnName("existing_view", "test"); QualifiedName missingColumnName = qualifiedColumnName("existing_view", "missing"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertThat(metadata.isView(testSession, viewName)).isTrue(); getFutureValue(setComment(COLUMN, columnName, Optional.of("new test column comment"))); @@ -149,7 +149,7 @@ public void testCommentViewColumn() public void testCommentOnMixedCaseViewColumn() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, viewDefinition("SELECT 1", ImmutableList.of(new ViewColumn("Mixed", BIGINT.getTypeId(), Optional.empty()))), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, viewDefinition("SELECT 1", ImmutableList.of(new ViewColumn("Mixed", BIGINT.getTypeId(), Optional.empty()))), ImmutableMap.of(), FAIL); assertThat(metadata.isView(testSession, viewName)).isTrue(); QualifiedName columnNameLowerCase = qualifiedColumnName("existing_view", "mixed"); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCreateBranchTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCreateBranchTask.java index f050837e9601..604d5ee138bb 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCreateBranchTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCreateBranchTask.java @@ -162,7 +162,7 @@ void testCreateBranchOnNotExistingTable() void testCreateBranchOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateBranch(asQualifiedName(viewName), SaveMode.FAIL, "main", Optional.empty(), List.of()))) .hasErrorCode(NOT_SUPPORTED) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java index 5989181eb92c..32c14fde9193 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCreateViewTask.java @@ -34,6 +34,7 @@ import io.trino.sql.tree.Property; import io.trino.sql.tree.QualifiedName; import io.trino.sql.tree.Query; +import io.trino.sql.tree.SaveMode; import io.trino.sql.tree.StringLiteral; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -45,12 +46,14 @@ import static io.airlift.concurrent.MoreFutures.getFutureValue; import static io.trino.spi.StandardErrorCode.INVALID_VIEW_PROPERTY; import static io.trino.spi.StandardErrorCode.TABLE_ALREADY_EXISTS; -import static io.trino.spi.connector.SaveMode.FAIL; import static io.trino.spi.session.PropertyMetadata.booleanProperty; import static io.trino.sql.QueryUtil.selectList; import static io.trino.sql.QueryUtil.simpleQuery; import static io.trino.sql.QueryUtil.table; import static io.trino.sql.analyzer.StatementAnalyzerFactory.createTestingStatementAnalyzerFactory; +import static io.trino.sql.tree.SaveMode.FAIL; +import static io.trino.sql.tree.SaveMode.IGNORE; +import static io.trino.sql.tree.SaveMode.REPLACE; import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD; @@ -78,14 +81,14 @@ public void setUp() new StatementRewrite(ImmutableSet.of()), plannerContext.getTracer()); QualifiedObjectName tableName = qualifiedObjectName("mock_table"); - metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), FAIL); + metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), io.trino.spi.connector.SaveMode.FAIL); } @Test public void testCreateViewOnViewIfNotExists() { QualifiedObjectName viewName = qualifiedObjectName("new_view"); - getFutureValue(executeCreateView(asQualifiedName(viewName), false)); + getFutureValue(executeCreateView(asQualifiedName(viewName), FAIL)); assertThat(metadata.isView(testSession, viewName)).isTrue(); } @@ -93,20 +96,30 @@ public void testCreateViewOnViewIfNotExists() public void testCreateViewOnViewIfExists() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), io.trino.spi.connector.SaveMode.FAIL); - assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(viewName), false))) + assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(viewName), FAIL))) .hasErrorCode(TABLE_ALREADY_EXISTS) .hasMessage("line 1:1: View already exists: '%s'", viewName); } + @Test + public void testCreateViewIfNotExistsOnExistingView() + { + QualifiedObjectName viewName = qualifiedObjectName("existing_view"); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), io.trino.spi.connector.SaveMode.FAIL); + + getFutureValue(executeCreateView(asQualifiedName(viewName), IGNORE)); + assertThat(metadata.isView(testSession, viewName)).isTrue(); + } + @Test public void testReplaceViewOnViewIfExists() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), io.trino.spi.connector.SaveMode.FAIL); - getFutureValue(executeCreateView(asQualifiedName(viewName), true)); + getFutureValue(executeCreateView(asQualifiedName(viewName), REPLACE)); assertThat(metadata.isView(testSession, viewName)).isTrue(); } @@ -114,22 +127,22 @@ public void testReplaceViewOnViewIfExists() public void testCreateViewOnTableIfExists() { QualifiedObjectName tableName = qualifiedObjectName("existing_table"); - metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), FAIL); + metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), io.trino.spi.connector.SaveMode.FAIL); - assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(tableName), false))) + assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(tableName), FAIL))) .hasErrorCode(TABLE_ALREADY_EXISTS) - .hasMessage("line 1:1: Table already exists: '%s'", tableName, tableName); + .hasMessage("line 1:1: Table already exists: '%s'", tableName); } @Test public void testReplaceViewOnTableIfExists() { QualifiedObjectName tableName = qualifiedObjectName("existing_table"); - metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), FAIL); + metadata.createTable(testSession, CATALOG_NAME, someTable(tableName), io.trino.spi.connector.SaveMode.FAIL); - assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(tableName), true))) + assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(tableName), REPLACE))) .hasErrorCode(TABLE_ALREADY_EXISTS) - .hasMessage("line 1:1: Table already exists: '%s'", tableName, tableName); + .hasMessage("line 1:1: Table already exists: '%s'", tableName); } @Test @@ -138,7 +151,7 @@ public void testCreateViewOnMaterializedView() QualifiedObjectName viewName = qualifiedObjectName("existing_materialized_view"); metadata.createMaterializedView(testSession, viewName, someMaterializedView(), MATERIALIZED_VIEW_PROPERTIES, false, false); - assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(viewName), false))) + assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView(asQualifiedName(viewName), FAIL))) .hasErrorCode(TABLE_ALREADY_EXISTS) .hasMessage("line 1:1: Materialized view already exists: '%s'", viewName); } @@ -151,7 +164,7 @@ public void testCreateViewWithUnknownProperty() assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView( asQualifiedName(viewName), ImmutableList.of(new Property(new NodeLocation(1, 88), new Identifier("unknown_property"), new StringLiteral("unknown"))), - false))) + FAIL))) .hasErrorCode(INVALID_VIEW_PROPERTY) .hasMessage("line 1:88: Catalog 'test_catalog' view property 'unknown_property' does not exist"); } @@ -164,24 +177,25 @@ public void testCreateViewWithInvalidProperty() assertTrinoExceptionThrownBy(() -> getFutureValue(executeCreateView( asQualifiedName(viewName), ImmutableList.of(new Property(new NodeLocation(1, 88), new Identifier("boolean_property"), new StringLiteral("unknown"))), - false))) + FAIL))) .hasErrorCode(INVALID_VIEW_PROPERTY) .hasMessage("line 1:88: Invalid value for catalog 'test_catalog' view property 'boolean_property': Cannot convert ['unknown'] to boolean"); } - private ListenableFuture executeCreateView(QualifiedName viewName, boolean replace) + private ListenableFuture executeCreateView(QualifiedName viewName, SaveMode saveMode) { - return executeCreateView(viewName, ImmutableList.of(), replace); + return executeCreateView(viewName, ImmutableList.of(), saveMode); } - private ListenableFuture executeCreateView(QualifiedName viewName, List viewProperties, boolean replace) + private ListenableFuture executeCreateView(QualifiedName viewName, List viewProperties, SaveMode saveMode) { - Query query = simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("mock_table"))); + Query query = simpleQuery(selectList(new AllColumns(new NodeLocation(1, 1))), table(QualifiedName.of("mock_table"))); + CreateView statement = new CreateView( new NodeLocation(1, 1), viewName, query, - replace, + saveMode, Optional.empty(), Optional.empty(), viewProperties); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropBranchTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropBranchTask.java index 7396af1c2761..a0d8141d8fb3 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropBranchTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropBranchTask.java @@ -90,7 +90,7 @@ void testDropBranchOnNotExistingTable() void testDropBranchOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropBranch(asQualifiedName(viewName), false, "main"))) .hasErrorCode(NOT_SUPPORTED) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropColumnTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropColumnTask.java index e467d25af63f..74fbb2b9959c 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropColumnTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropColumnTask.java @@ -147,7 +147,7 @@ public void testUnsupportedDropOnlyField() public void testDropColumnOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropColumn(asQualifiedName(viewName), QualifiedName.of("test"), false, false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropDefaultValueTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropDefaultValueTask.java index e01754d10b6b..de2537c5e8cd 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropDefaultValueTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropDefaultValueTask.java @@ -132,7 +132,7 @@ void testDropNonDefaultValueColumn() void testDropDefaultValueOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropDefaultValue(asQualifiedName(viewName), "test", false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropMaterializedViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropMaterializedViewTask.java index 83e751f74aae..be217bffe347 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropMaterializedViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropMaterializedViewTask.java @@ -90,7 +90,7 @@ public void testDropMaterializedViewOnTableIfExists() public void testDropMaterializedViewOnView() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropMaterializedView(viewName, false))) .hasErrorCode(GENERIC_USER_ERROR) @@ -101,7 +101,7 @@ public void testDropMaterializedViewOnView() public void testDropMaterializedViewOnViewIfExists() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropMaterializedView(viewName, false))) .hasErrorCode(GENERIC_USER_ERROR) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropNotNullConstraintConstraintTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropNotNullConstraintConstraintTask.java index 2a0e35306b28..c6c39ce94f71 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropNotNullConstraintConstraintTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropNotNullConstraintConstraintTask.java @@ -122,7 +122,7 @@ public void testDropNotNullConstraintNullableColumn() public void testDropNotNullConstraintOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropNotNullConstraint(asQualifiedName(viewName), identifier("test"), false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropTableTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropTableTask.java index 0f255ffae8e8..1c9ecf919636 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropTableTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropTableTask.java @@ -87,7 +87,7 @@ public void testDropTableIfExistsWithoutExistingTable() public void testDropTableOnView() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropTable(viewName, false))) .hasErrorCode(GENERIC_USER_ERROR) @@ -98,7 +98,7 @@ public void testDropTableOnView() public void testDropTableIfExistsOnView() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, asQualifiedObjectName(viewName), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeDropTable(viewName, true))) .hasErrorCode(GENERIC_USER_ERROR) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDropViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDropViewTask.java index 3ae5bdc4d15f..cf6dfcb39ed8 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDropViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDropViewTask.java @@ -38,7 +38,7 @@ public class TestDropViewTask public void testDropExistingView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertThat(metadata.isView(testSession, viewName)).isTrue(); getFutureValue(executeDropView(asQualifiedName(viewName), false)); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestFastForwardBranchTask.java b/core/trino-main/src/test/java/io/trino/execution/TestFastForwardBranchTask.java index 1739b4cef96e..f7453dbbe593 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestFastForwardBranchTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestFastForwardBranchTask.java @@ -78,7 +78,7 @@ void testFastForwardBranchOnNotExistingTable() void testFastForwardBranchOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeFastForwardBranch(asQualifiedName(viewName), "main", "main"))) .hasErrorCode(NOT_SUPPORTED) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRefreshViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRefreshViewTask.java index 56752bc25cdf..3ddb093338e5 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestRefreshViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestRefreshViewTask.java @@ -98,7 +98,7 @@ void testAddNewColumnsOnTableWhenRefreshingExistingView() Optional.empty(), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); metadata.addColumn( testSession, @@ -141,7 +141,7 @@ void testDropColumnsOnTableWhenRefreshingExistingView() Optional.empty(), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); TableHandle tableHandle = metadata.getTableHandle(testSession, tableName).orElseThrow(); metadata.dropColumn( @@ -172,7 +172,7 @@ void testRenameColumnsOnTableWhenRefreshingExistingView() Optional.empty(), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); TableHandle tableHandle = metadata.getTableHandle(testSession, tableName).orElseThrow(); metadata.renameColumn( @@ -204,7 +204,7 @@ void testColumnTypeChangeOnTableWhenRefreshingExistingView() Optional.empty(), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); TableHandle tableHandle = metadata.getTableHandle(testSession, tableName).orElseThrow(); metadata.setColumnType( @@ -235,7 +235,7 @@ void testTableDroppedWhenRefreshingExistingView() Optional.empty(), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); TableHandle tableHandle = metadata.getTableHandle(testSession, tableName).orElseThrow(); metadata.dropTable(testSession, tableHandle, tableName.asCatalogSchemaTableName()); @@ -262,7 +262,7 @@ void testRefreshOnInvokerViewWithRevokedAccessForTable() Optional.empty(), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); assertThatThrownBy(() -> getFutureValue( executeRefreshView( @@ -289,7 +289,7 @@ void testRefreshOnDefinerViewWithRevokedAccessForTable() Optional.of(Identity.ofUser("owner")), ImmutableList.of()), ImmutableMap.of(), - false); + FAIL); assertThatThrownBy(() -> getFutureValue( executeRefreshView(asQualifiedName( diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java index 8a75ef19f4e6..feff903b6e0c 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java @@ -107,7 +107,7 @@ public void testRenameColumnIfExists() public void testRenameColumnOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameColumn(asQualifiedName(viewName), QualifiedName.of("a"), identifier("a_renamed"), false, false))) .hasErrorCode(TABLE_NOT_FOUND) @@ -201,7 +201,7 @@ public void testUnsupportedRenameToExistingField() public void testRenameFieldOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameColumn(asQualifiedName(viewName), QualifiedName.of("test"), identifier("x"), false, false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRenameMaterializedViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRenameMaterializedViewTask.java index 21fb1ed3f3d5..dc438a0f7e3e 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestRenameMaterializedViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestRenameMaterializedViewTask.java @@ -105,7 +105,7 @@ public void testRenameMaterializedViewTargetTableExists() public void testRenameMaterializedViewOnView() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameMaterializedView(viewName, qualifiedName("existing_view_new")))) .hasErrorCode(TABLE_NOT_FOUND) @@ -116,7 +116,7 @@ public void testRenameMaterializedViewOnView() public void testRenameMaterializedViewOnViewIfExists() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameMaterializedView(viewName, qualifiedName("existing_view_new"), true))) .hasErrorCode(TABLE_NOT_FOUND) @@ -129,7 +129,7 @@ public void testRenameMaterializedViewTargetViewExists() QualifiedObjectName materializedViewName = qualifiedObjectName("existing_materialized_view"); metadata.createMaterializedView(testSession, materializedViewName, someMaterializedView(), MATERIALIZED_VIEW_PROPERTIES, false, false); QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameMaterializedView(asQualifiedName(materializedViewName), viewName))) .hasErrorCode(TABLE_ALREADY_EXISTS) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRenameTableTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRenameTableTask.java index 97ba0e1ee2dc..c7572a54916e 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestRenameTableTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestRenameTableTask.java @@ -70,7 +70,7 @@ public void testRenameNotExistingTableIfExists() public void testRenameTableOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameTable(asQualifiedName(viewName), qualifiedName("existing_view_new"), false))) .hasErrorCode(GENERIC_USER_ERROR) @@ -81,7 +81,7 @@ public void testRenameTableOnView() public void testRenameTableOnViewIfExists() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameTable(asQualifiedName(viewName), qualifiedName("existing_view_new"), true))) .hasErrorCode(GENERIC_USER_ERROR) @@ -116,7 +116,7 @@ public void testRenameTableTargetViewExists() QualifiedObjectName tableName = qualifiedObjectName("existing_table"); metadata.createTable(testSession, TEST_CATALOG_NAME, someTable(tableName), FAIL); QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameTable(asQualifiedName(tableName), viewName, false))) .hasErrorCode(GENERIC_USER_ERROR) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRenameViewTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRenameViewTask.java index 19a6b84d5d5b..b86db3b81830 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestRenameViewTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestRenameViewTask.java @@ -41,7 +41,7 @@ public void testRenameExistingView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); QualifiedObjectName newViewName = qualifiedObjectName("existing_view_new"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); getFutureValue(executeRenameView(asQualifiedName(viewName), asQualifiedName(newViewName))); assertThat(metadata.isView(testSession, viewName)).isFalse(); @@ -84,7 +84,7 @@ public void testRenameViewOnMaterializedView() public void testRenameViewTargetTableExists() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); QualifiedObjectName tableName = qualifiedObjectName("existing_table"); metadata.createTable(testSession, TEST_CATALOG_NAME, someTable(tableName), FAIL); @@ -97,7 +97,7 @@ public void testRenameViewTargetTableExists() public void testRenameViewTargetMaterializedViewExists() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); QualifiedObjectName materializedViewName = qualifiedObjectName("existing_materialized_view"); metadata.createMaterializedView(testSession, materializedViewName, someMaterializedView(), MATERIALIZED_VIEW_PROPERTIES, false, false); @@ -110,7 +110,7 @@ public void testRenameViewTargetMaterializedViewExists() public void testRenameViewTargetViewExists() { QualifiedName viewName = qualifiedName("existing_view"); - metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), false); + metadata.createView(testSession, QualifiedObjectName.valueOf(viewName.toString()), someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameView(viewName, viewName))) .hasErrorCode(GENERIC_USER_ERROR) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSetColumnTypeTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSetColumnTypeTask.java index 07d1f49e70d0..0392ddd7e42d 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSetColumnTypeTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSetColumnTypeTask.java @@ -103,7 +103,7 @@ public void testSetDataTypeNotExistingColumn() public void testSetDataTypeOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeSetColumnType(asQualifiedName(viewName), QualifiedName.of("test"), toSqlType(INTEGER), false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSetDefaultValueTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSetDefaultValueTask.java index e9055a5170e8..4e3b247decf5 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSetDefaultValueTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSetDefaultValueTask.java @@ -146,7 +146,7 @@ void testSetDefaultValueMissingColumn() void testSetDefaultValueOnView() { QualifiedObjectName viewName = qualifiedObjectName("existing_view"); - metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), false); + metadata.createView(testSession, viewName, someView(), ImmutableMap.of(), FAIL); assertTrinoExceptionThrownBy(() -> getFutureValue(executeSetDefaultValue(asQualifiedName(viewName), "test", "123", false))) .hasErrorCode(TABLE_NOT_FOUND) diff --git a/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java b/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java index 056a98aeb6ca..94072a78798c 100644 --- a/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java +++ b/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java @@ -665,7 +665,7 @@ public Optional getSchemaOwner(Session session, CatalogSchemaNam } @Override - public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map viewProperties, boolean replace) + public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, Map viewProperties, SaveMode saveMode) { throw new UnsupportedOperationException(); } 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 fad7860e0368..c6c47f4b4e5b 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 @@ -8365,7 +8365,7 @@ public void setup() Optional.of("comment"), Optional.of(Identity.ofUser("user")), ImmutableList.of()); - inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v1"), viewData1, ImmutableMap.of(), false)); + inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v1"), viewData1, ImmutableMap.of(), FAIL)); // stale view (different column type) ViewDefinition viewData2 = new ViewDefinition( @@ -8376,7 +8376,7 @@ public void setup() Optional.of("comment"), Optional.of(Identity.ofUser("user")), ImmutableList.of()); - inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v2"), viewData2, ImmutableMap.of(), false)); + inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v2"), viewData2, ImmutableMap.of(), FAIL)); // valid view with uppercase column name ViewDefinition viewData4 = new ViewDefinition( @@ -8387,7 +8387,7 @@ public void setup() Optional.of("comment"), Optional.of(Identity.ofUser("user")), ImmutableList.of()); - inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName("tpch", "s1", "v4"), viewData4, ImmutableMap.of(), false)); + inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName("tpch", "s1", "v4"), viewData4, ImmutableMap.of(), FAIL)); // recursive view referencing to itself ViewDefinition viewData5 = new ViewDefinition( @@ -8398,7 +8398,7 @@ public void setup() Optional.of("comment"), Optional.of(Identity.ofUser("user")), ImmutableList.of()); - inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v5"), viewData5, ImmutableMap.of(), false)); + inSetupTransaction(session -> metadata.createView(session, new QualifiedObjectName(TPCH_CATALOG, "s1", "v5"), viewData5, ImmutableMap.of(), FAIL)); // type analysis for INSERT SchemaTableName table8 = new SchemaTableName("s1", "t8"); @@ -8494,7 +8494,7 @@ public void setup() tableViewAndMaterializedView, viewDefinition, ImmutableMap.of(), - false)); + FAIL)); inSetupTransaction(session -> metadata.createTable( session, CATALOG_FOR_IDENTIFIER_CHAIN_TESTS, @@ -8509,7 +8509,7 @@ public void setup() tableAndView, viewDefinition, ImmutableMap.of(), - false)); + FAIL)); inSetupTransaction(session -> metadata.createTable( session, CATALOG_FOR_IDENTIFIER_CHAIN_TESTS, 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 f062582df352..b11cfaafed0a 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 @@ -1215,7 +1215,11 @@ protected Void visitCreateView(CreateView node, Integer indent) if (node.isReplace()) { builder.append("OR REPLACE "); } - builder.append("VIEW ") + builder.append("VIEW "); + if (node.isNotExists()) { + builder.append("IF NOT EXISTS "); + } + builder .append(formatName(node.getName())); node.getComment().ifPresent(comment -> builder 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 86423c5a89bb..a5d022144f79 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 @@ -964,6 +964,10 @@ public Node visitTableExecute(SqlBaseParser.TableExecuteContext context) @Override public Node visitCreateView(SqlBaseParser.CreateViewContext context) { + if (context.REPLACE() != null && context.EXISTS() != null) { + throw parseError("'OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together", context); + } + Optional comment = Optional.empty(); if (context.COMMENT() != null) { comment = Optional.of(visitString(context.string()).getValue()); @@ -986,7 +990,7 @@ else if (context.INVOKER() != null) { getLocation(context), getQualifiedName(context.qualifiedName()), (Query) visit(context.rootQuery()), - context.REPLACE() != null, + toSaveMode(context.REPLACE(), context.EXISTS()), comment, security, properties); diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CreateView.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CreateView.java index 19acab707702..2d1de4e1b777 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/CreateView.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CreateView.java @@ -32,17 +32,17 @@ public enum Security private final QualifiedName name; private final Query query; - private final boolean replace; + private final SaveMode saveMode; private final Optional comment; private final Optional security; private final List properties; - public CreateView(NodeLocation location, QualifiedName name, Query query, boolean replace, Optional comment, Optional security, List properties) + public CreateView(NodeLocation location, QualifiedName name, Query query, SaveMode saveMode, Optional comment, Optional security, List properties) { super(location); this.name = requireNonNull(name, "name is null"); this.query = requireNonNull(query, "query is null"); - this.replace = replace; + this.saveMode = requireNonNull(saveMode, "saveMode is null"); this.comment = requireNonNull(comment, "comment is null"); this.security = requireNonNull(security, "security is null"); this.properties = ImmutableList.copyOf(properties); @@ -58,9 +58,19 @@ public Query getQuery() return query; } + public SaveMode getSaveMode() + { + return saveMode; + } + public boolean isReplace() { - return replace; + return saveMode == SaveMode.REPLACE; + } + + public boolean isNotExists() + { + return saveMode == SaveMode.IGNORE; } public Optional getComment() @@ -96,7 +106,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(name, query, replace, security, properties); + return Objects.hash(name, query, saveMode, comment, security, properties); } @Override @@ -111,7 +121,7 @@ public boolean equals(Object obj) CreateView o = (CreateView) obj; return Objects.equals(name, o.name) && Objects.equals(query, o.query) - && replace == o.replace + && saveMode == o.saveMode && Objects.equals(comment, o.comment) && Objects.equals(security, o.security) && Objects.equals(properties, o.properties); @@ -123,7 +133,7 @@ public String toString() return toStringHelper(this) .add("name", name) .add("query", query) - .add("replace", replace) + .add("saveMode", saveMode) .add("comment", comment) .add("security", security) .add("properties", properties) 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 45517ffbf8c0..f33405f53bc9 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 @@ -374,7 +374,7 @@ public void testCreateView() new NodeLocation(1, 1), QualifiedName.of("test"), simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("t"))), - false, + FAIL, Optional.empty(), Optional.empty(), ImmutableList.of()))) @@ -387,7 +387,20 @@ public void testCreateView() new NodeLocation(1, 1), QualifiedName.of("test"), simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("t"))), - false, + IGNORE, + Optional.empty(), + Optional.empty(), + ImmutableList.of()))) + .isEqualTo("CREATE VIEW IF NOT EXISTS test AS\n" + + "SELECT *\n" + + "FROM\n" + + " t\n"); + assertThat(formatSql( + new CreateView( + new NodeLocation(1, 1), + QualifiedName.of("test"), + simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("t"))), + FAIL, Optional.of("攻殻機動隊"), Optional.empty(), ImmutableList.of()))) @@ -402,7 +415,7 @@ public void testCreateView() new NodeLocation(1, 1), QualifiedName.of("test"), simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("t"))), - false, + FAIL, Optional.empty(), Optional.empty(), ImmutableList.of( @@ -426,7 +439,7 @@ public void testCreateView() new NodeLocation(1, 1), QualifiedName.of("test"), simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("t"))), - false, + FAIL, Optional.of("攻殻機動隊"), Optional.of(DEFINER), ImmutableList.of(new Property(new Identifier("property"), new StringLiteral("property_value")))))) 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 0b30a9562e64..0dc442db88ee 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 @@ -185,6 +185,7 @@ import io.trino.sql.tree.RevokeRoles; import io.trino.sql.tree.Rollback; import io.trino.sql.tree.Row; +import io.trino.sql.tree.SaveMode; import io.trino.sql.tree.SearchedCaseExpression; import io.trino.sql.tree.Select; import io.trino.sql.tree.SelectItem; @@ -4380,17 +4381,18 @@ public void testCreateView() { Query query = simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("t"))); - assertStatement("CREATE VIEW a AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.empty(), Optional.empty(), ImmutableList.of())); - assertStatement("CREATE OR REPLACE VIEW a AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, true, Optional.empty(), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW a AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.empty(), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW IF NOT EXISTS a AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.IGNORE, Optional.empty(), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE OR REPLACE VIEW a AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.REPLACE, Optional.empty(), Optional.empty(), ImmutableList.of())); - assertStatement("CREATE VIEW a SECURITY DEFINER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.empty(), Optional.of(CreateView.Security.DEFINER), ImmutableList.of())); - assertStatement("CREATE VIEW a SECURITY INVOKER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.empty(), Optional.of(CreateView.Security.INVOKER), ImmutableList.of())); + assertStatement("CREATE VIEW a SECURITY DEFINER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.empty(), Optional.of(CreateView.Security.DEFINER), ImmutableList.of())); + assertStatement("CREATE VIEW a SECURITY INVOKER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.empty(), Optional.of(CreateView.Security.INVOKER), ImmutableList.of())); - assertStatement("CREATE VIEW a COMMENT 'comment' SECURITY DEFINER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.of("comment"), Optional.of(CreateView.Security.DEFINER), ImmutableList.of())); - assertStatement("CREATE VIEW a COMMENT '' SECURITY INVOKER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.of(""), Optional.of(CreateView.Security.INVOKER), ImmutableList.of())); + assertStatement("CREATE VIEW a COMMENT 'comment' SECURITY DEFINER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.of("comment"), Optional.of(CreateView.Security.DEFINER), ImmutableList.of())); + assertStatement("CREATE VIEW a COMMENT '' SECURITY INVOKER AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.of(""), Optional.of(CreateView.Security.INVOKER), ImmutableList.of())); - assertStatement("CREATE VIEW a COMMENT 'comment' AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.of("comment"), Optional.empty(), ImmutableList.of())); - assertStatement("CREATE VIEW a COMMENT '' AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, false, Optional.of(""), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW a COMMENT 'comment' AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.of("comment"), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW a COMMENT '' AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("a"), query, SaveMode.FAIL, Optional.of(""), Optional.empty(), ImmutableList.of())); assertStatement( "CREATE VIEW a WITH (property_1 = 'value_1', property_2 = 2) AS SELECT * FROM t", @@ -4398,16 +4400,19 @@ public void testCreateView() location(1, 1), QualifiedName.of("a"), query, - false, + SaveMode.FAIL, Optional.empty(), Optional.empty(), ImmutableList.of( new Property(new Identifier("property_1"), new StringLiteral("value_1")), new Property(new Identifier("property_2"), new LongLiteral("2"))))); - assertStatement("CREATE VIEW bar.foo AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("bar", "foo"), query, false, Optional.empty(), Optional.empty(), ImmutableList.of())); - assertStatement("CREATE VIEW \"awesome view\" AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("awesome view"), query, false, Optional.empty(), Optional.empty(), ImmutableList.of())); - assertStatement("CREATE VIEW \"awesome schema\".\"awesome view\" AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("awesome schema", "awesome view"), query, false, Optional.empty(), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW bar.foo AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("bar", "foo"), query, SaveMode.FAIL, Optional.empty(), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW \"awesome view\" AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("awesome view"), query, SaveMode.FAIL, Optional.empty(), Optional.empty(), ImmutableList.of())); + assertStatement("CREATE VIEW \"awesome schema\".\"awesome view\" AS SELECT * FROM t", new CreateView(location(1, 1), QualifiedName.of("awesome schema", "awesome view"), query, SaveMode.FAIL, Optional.empty(), Optional.empty(), ImmutableList.of())); + + assertStatementIsInvalid("CREATE OR REPLACE VIEW IF NOT EXISTS a AS SELECT * FROM t") + .withMessage("line 1:1: 'OR REPLACE' and 'IF NOT EXISTS' clauses can not be used together"); } @Test diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java index 7e68c2502ea0..03f5ca63265b 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java @@ -941,11 +941,20 @@ default void finishMerge( throw new TrinoException(GENERIC_INTERNAL_ERROR, "ConnectorMetadata beginMerge() is implemented without finishMerge()"); } + /** + * @deprecated use {@link #createView(ConnectorSession, SchemaTableName, ConnectorViewDefinition, Map, SaveMode)} instead. + */ + @Deprecated + default void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + { + throw new TrinoException(NOT_SUPPORTED, "This connector does not support creating views"); + } + /** * Create the specified view. The view definition is intended to * be serialized by the connector for permanent storage. */ - default void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + default void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) { throw new TrinoException(NOT_SUPPORTED, "This connector does not support creating views"); } diff --git a/docs/src/main/sphinx/sql/create-view.md b/docs/src/main/sphinx/sql/create-view.md index 70cd167a7cc8..e608866db734 100644 --- a/docs/src/main/sphinx/sql/create-view.md +++ b/docs/src/main/sphinx/sql/create-view.md @@ -3,7 +3,7 @@ ## Synopsis ```text -CREATE [ OR REPLACE ] VIEW view_name +CREATE [ OR REPLACE ] VIEW [ IF NOT EXISTS ] view_name [ COMMENT view_comment ] [ SECURITY { DEFINER | INVOKER } ] AS query @@ -19,6 +19,11 @@ referenced by another query. The optional `OR REPLACE` clause causes the view to be replaced if it already exists rather than raising an error. +The optional `IF NOT EXISTS` clause causes the error to be +suppressed if the view already exists. + +`OR REPLACE` and `IF NOT EXISTS` cannot be used together. + ## Security In the default `DEFINER` security mode, tables referenced in the view diff --git a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java index 9c787b2164da..a34d8d72a6de 100644 --- a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java +++ b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java @@ -672,6 +672,7 @@ public Optional finishRefreshMaterializedView(Connector } @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) { try (ThreadContextClassLoader _ = new ThreadContextClassLoader(classLoader)) { @@ -679,6 +680,14 @@ public void createView(ConnectorSession session, SchemaTableName viewName, Conne } } + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) + { + try (ThreadContextClassLoader _ = new ThreadContextClassLoader(classLoader)) { + delegate.createView(session, viewName, definition, viewProperties, saveMode); + } + } + @Override public void renameView(ConnectorSession session, SchemaTableName source, SchemaTableName target) { diff --git a/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java b/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java index 3125e154e7e3..5f381ff144ab 100644 --- a/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java +++ b/plugin/trino-blackhole/src/main/java/io/trino/plugin/blackhole/BlackHoleMetadata.java @@ -450,10 +450,22 @@ public void finishMerge(ConnectorSession session, ConnectorMergeTableHandle merg public void truncateTable(ConnectorSession session, ConnectorTableHandle tableHandle) {} @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + { + createView(session, viewName, definition, viewProperties, replace ? SaveMode.REPLACE : SaveMode.FAIL); + } + + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) { checkArgument(viewProperties.isEmpty(), "This connector does not support creating views with properties"); - views.put(viewName, definition); + if (saveMode == SaveMode.REPLACE) { + views.put(viewName, definition); + } + else if (saveMode == SaveMode.FAIL && views.putIfAbsent(viewName, definition) != null) { + throw new TrinoException(ALREADY_EXISTS, "View already exists: " + viewName); + } } @Override diff --git a/plugin/trino-blackhole/src/test/java/io/trino/plugin/blackhole/TestBlackHoleMetadata.java b/plugin/trino-blackhole/src/test/java/io/trino/plugin/blackhole/TestBlackHoleMetadata.java index c5c7c0222b81..15d7c0fd7dea 100644 --- a/plugin/trino-blackhole/src/test/java/io/trino/plugin/blackhole/TestBlackHoleMetadata.java +++ b/plugin/trino-blackhole/src/test/java/io/trino/plugin/blackhole/TestBlackHoleMetadata.java @@ -19,6 +19,7 @@ import io.trino.spi.connector.ConnectorOutputTableHandle; import io.trino.spi.connector.ConnectorTableMetadata; import io.trino.spi.connector.ConnectorViewDefinition; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.security.TrinoPrincipal; import org.junit.jupiter.api.Test; @@ -99,7 +100,7 @@ void testGetView() .buildOrThrow(); for (Entry entry : views.entrySet()) { - metadata.createView(SESSION, entry.getKey(), entry.getValue(), ImmutableMap.of(), false); + metadata.createView(SESSION, entry.getKey(), entry.getValue(), ImmutableMap.of(), SaveMode.FAIL); } assertThat(metadata.listViews(SESSION, Optional.empty())) diff --git a/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/DeltaLakeMetadata.java b/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/DeltaLakeMetadata.java index 9e7b1ab30b66..f9609d181f6f 100644 --- a/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/DeltaLakeMetadata.java +++ b/plugin/trino-delta-lake/src/main/java/io/trino/plugin/deltalake/DeltaLakeMetadata.java @@ -3437,10 +3437,17 @@ public Map getSchemaProperties(ConnectorSession session, String } @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + { + createView(session, viewName, definition, viewProperties, replace ? SaveMode.REPLACE : SaveMode.FAIL); + } + + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) { checkArgument(viewProperties.isEmpty(), "This connector does not support creating views with properties"); - trinoViewHiveMetastore.createView(session, viewName, definition, replace); + trinoViewHiveMetastore.createView(session, viewName, definition, saveMode); } @Override diff --git a/plugin/trino-faker/src/main/java/io/trino/plugin/faker/FakerMetadata.java b/plugin/trino-faker/src/main/java/io/trino/plugin/faker/FakerMetadata.java index 6ef9a92330c0..9965c5b43e86 100644 --- a/plugin/trino-faker/src/main/java/io/trino/plugin/faker/FakerMetadata.java +++ b/plugin/trino-faker/src/main/java/io/trino/plugin/faker/FakerMetadata.java @@ -665,13 +665,13 @@ public synchronized void createView( SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, - boolean replace) + SaveMode saveMode) { checkArgument(viewProperties.isEmpty(), "This connector does not support creating views with properties"); checkSchemaExists(viewName.getSchemaName()); checkTableNotExists(viewName); - if (replace) { + if (saveMode == SaveMode.REPLACE) { views.put(viewName, definition); } else if (views.putIfAbsent(viewName, definition) != null) { diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java index c5c50651f80d..10188561d2a6 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java @@ -2778,7 +2778,7 @@ public void createView( SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, - boolean replace) + SaveMode saveMode) { if (usingSystemSecurity) { definition = definition.withoutOwner(); @@ -2830,7 +2830,7 @@ public void createView( Optional existing = metastore.getTable(viewName.getSchemaName(), viewName.getTableName()); if (existing.isPresent()) { - if (!replace || !isTrinoView(existing.get())) { + if (saveMode != REPLACE || !isTrinoView(existing.get())) { throw new ViewAlreadyExistsException(viewName); } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/TrinoViewHiveMetastore.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/TrinoViewHiveMetastore.java index 1ceeb04f7ee8..e64b332beaab 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/TrinoViewHiveMetastore.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/TrinoViewHiveMetastore.java @@ -24,6 +24,7 @@ import io.trino.spi.TrinoException; import io.trino.spi.connector.ConnectorSession; import io.trino.spi.connector.ConnectorViewDefinition; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; import io.trino.spi.connector.ViewNotFoundException; @@ -63,7 +64,7 @@ public TrinoViewHiveMetastore(HiveMetastore metastore, boolean isUsingSystemSecu this.connectorName = requireNonNull(connectorName, "connectorName is null"); } - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { if (isUsingSystemSecurity) { definition = definition.withoutOwner(); @@ -88,7 +89,7 @@ public void createView(ConnectorSession session, SchemaTableName schemaViewName, Optional
existing = metastore.getTable(schemaViewName.getSchemaName(), schemaViewName.getTableName()); if (existing.isPresent()) { - if (!replace || !isTrinoView(existing.get())) { + if (saveMode != SaveMode.REPLACE || !isTrinoView(existing.get())) { throw new ViewAlreadyExistsException(schemaViewName); } diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java index 942cae8669f3..4efc4383f3b4 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java @@ -3754,10 +3754,17 @@ static TupleDomain extractTupleDomainsFromCommitTasks(Icebe } @Override + @Deprecated public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + { + createView(session, viewName, definition, viewProperties, replace ? SaveMode.REPLACE : SaveMode.FAIL); + } + + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) { checkArgument(viewProperties.isEmpty(), "This connector does not support creating views with properties"); - catalog.createView(session, viewName, definition, replace); + catalog.createView(session, viewName, definition, saveMode); } @Override diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/TrinoCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/TrinoCatalog.java index 823c8b8f9d79..2847931df022 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/TrinoCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/TrinoCatalog.java @@ -23,6 +23,7 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.metrics.Metrics; import io.trino.spi.security.TrinoPrincipal; @@ -166,7 +167,7 @@ Transaction newCreateOrReplaceTableTransaction( void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTableName, TrinoPrincipal principal); - void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace); + void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode); void renameView(ConnectorSession session, SchemaTableName source, SchemaTableName target); 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 ffbf6e43d20a..4e0136be3f7d 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 @@ -47,6 +47,7 @@ import io.trino.spi.connector.MaterializedViewNotFoundException; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaNotFoundException; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; @@ -933,7 +934,7 @@ public void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTa } @Override - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { // If a view is created between listing the existing view and calling createTable, retry TableInput viewTableInput = getViewTableInput( @@ -944,17 +945,17 @@ public void createView(ConnectorSession session, SchemaTableName schemaViewName, Failsafe.with(RetryPolicy.builder() .withMaxRetries(3) .withDelay(Duration.ofMillis(100)) - .handleIf(throwable -> replace && !(throwable instanceof ViewAlreadyExistsException)) + .handleIf(throwable -> saveMode == SaveMode.REPLACE && !(throwable instanceof ViewAlreadyExistsException)) .abortOn(TrinoFileSystem::isUnrecoverableException) .build()) - .run(() -> doCreateView(session, schemaViewName, viewTableInput, replace)); + .run(() -> doCreateView(session, schemaViewName, viewTableInput, saveMode)); } - private void doCreateView(ConnectorSession session, SchemaTableName schemaViewName, TableInput viewTableInput, boolean replace) + private void doCreateView(ConnectorSession session, SchemaTableName schemaViewName, TableInput viewTableInput, SaveMode saveMode) { Optional
existing = getTableAndCacheMetadata(session, schemaViewName); if (existing.isPresent()) { - if (!replace || !isTrinoView(getTableType(existing.get()), existing.get().parameters())) { + if (saveMode != SaveMode.REPLACE || !isTrinoView(getTableType(existing.get()), existing.get().parameters())) { // TODO: ViewAlreadyExists is misleading if the name is used by a table https://github.com/trinodb/trino/issues/10037 throw new ViewAlreadyExistsException(schemaViewName); } 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 fe1f74d2b638..96d0e3be9f68 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 @@ -50,6 +50,7 @@ import io.trino.spi.connector.MaterializedViewNotFoundException; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaNotFoundException; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; @@ -525,9 +526,9 @@ public void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTa } @Override - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { - trinoViewHiveMetastore.createView(session, schemaViewName, definition, replace); + trinoViewHiveMetastore.createView(session, schemaViewName, definition, saveMode); } @Override diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/jdbc/TrinoJdbcCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/jdbc/TrinoJdbcCatalog.java index cd0497e5b74c..595b0ae0bd16 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/jdbc/TrinoJdbcCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/jdbc/TrinoJdbcCatalog.java @@ -35,6 +35,7 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; import io.trino.spi.connector.ViewNotFoundException; @@ -465,7 +466,7 @@ public void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTa } @Override - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { if (schemaVersion == SchemaVersion.V0) { throw new TrinoException(NOT_SUPPORTED, "Schema version V0 does not support views"); @@ -483,7 +484,7 @@ public void createView(ConnectorSession session, SchemaTableName schemaViewName, .withProperties(properties.buildOrThrow()) .withLocation(defaultTableLocation(session, schemaViewName)); - if (replace) { + if (saveMode == SaveMode.REPLACE) { viewBuilder.createOrReplace(); } else { diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/nessie/TrinoNessieCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/nessie/TrinoNessieCatalog.java index a18b0ac436eb..b146f774a7e9 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/nessie/TrinoNessieCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/nessie/TrinoNessieCatalog.java @@ -32,6 +32,7 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaNotFoundException; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; @@ -346,7 +347,7 @@ public void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTa } @Override - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { throw new TrinoException(NOT_SUPPORTED, "createView is not supported for Iceberg Nessie catalogs"); } diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/TrinoRestCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/TrinoRestCatalog.java index 600b4a59aa39..7f4bcff139a5 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/TrinoRestCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/rest/TrinoRestCatalog.java @@ -42,6 +42,7 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaNotFoundException; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; @@ -655,7 +656,7 @@ public void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTa } @Override - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { ImmutableMap.Builder properties = ImmutableMap.builder(); definition.getOwner().ifPresent(owner -> properties.put(ICEBERG_VIEW_RUN_AS_OWNER, owner)); @@ -669,7 +670,7 @@ public void createView(ConnectorSession session, SchemaTableName schemaViewName, .withProperties(properties.buildOrThrow()) .withLocation(defaultTableLocation(session, schemaViewName)); try { - if (replace) { + if (saveMode == SaveMode.REPLACE) { viewBuilder.createOrReplace(); } else { diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/snowflake/TrinoSnowflakeCatalog.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/snowflake/TrinoSnowflakeCatalog.java index c679a8f491f1..2677daac8a0a 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/snowflake/TrinoSnowflakeCatalog.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/catalog/snowflake/TrinoSnowflakeCatalog.java @@ -32,6 +32,7 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.RelationColumnsMetadata; import io.trino.spi.connector.RelationCommentMetadata; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.TableNotFoundException; import io.trino.spi.security.TrinoPrincipal; @@ -329,7 +330,7 @@ public void setTablePrincipal(ConnectorSession session, SchemaTableName schemaTa } @Override - public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, boolean replace) + public void createView(ConnectorSession session, SchemaTableName schemaViewName, ConnectorViewDefinition definition, SaveMode saveMode) { throw new TrinoException(NOT_SUPPORTED, "Views are not supported for the Snowflake Iceberg catalog"); } 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 37152a009bcf..622f5826f5a2 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 @@ -35,6 +35,7 @@ import io.trino.spi.connector.ConnectorMetadata; import io.trino.spi.connector.ConnectorSession; import io.trino.spi.connector.ConnectorViewDefinition; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.security.PrincipalType; import io.trino.spi.security.TrinoPrincipal; @@ -434,7 +435,7 @@ public void testView() try { catalog.createNamespace(SESSION, namespace, defaultNamespaceProperties(namespace), new TrinoPrincipal(PrincipalType.USER, SESSION.getUser())); - catalog.createView(SESSION, schemaTableName, viewDefinition, false); + catalog.createView(SESSION, schemaTableName, viewDefinition, SaveMode.FAIL); assertThat(catalog.listTables(SESSION, Optional.of(namespace)).stream()).contains(new TableInfo(schemaTableName, getViewType())); @@ -531,7 +532,7 @@ public void testListTables() Optional.of(SESSION.getUser()), false, ImmutableList.of()), - false); + SaveMode.FAIL); closer.register(() -> catalog.dropView(SESSION, view)); allTables.add(new TableInfo(view, getViewType())); } diff --git a/plugin/trino-iceberg/src/test/java/org/apache/iceberg/snowflake/TestTrinoSnowflakeCatalog.java b/plugin/trino-iceberg/src/test/java/org/apache/iceberg/snowflake/TestTrinoSnowflakeCatalog.java index 6bc50bf0d5c2..366e84c0f432 100644 --- a/plugin/trino-iceberg/src/test/java/org/apache/iceberg/snowflake/TestTrinoSnowflakeCatalog.java +++ b/plugin/trino-iceberg/src/test/java/org/apache/iceberg/snowflake/TestTrinoSnowflakeCatalog.java @@ -36,6 +36,7 @@ import io.trino.spi.catalog.CatalogName; import io.trino.spi.connector.ConnectorMetadata; import io.trino.spi.connector.ConnectorViewDefinition; +import io.trino.spi.connector.SaveMode; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.security.ConnectorIdentity; import io.trino.spi.type.VarcharType; @@ -358,7 +359,7 @@ public void testView() false, ImmutableList.of()); TrinoCatalog catalog = createTrinoCatalog(false); - assertThatThrownBy(() -> catalog.createView(SESSION, SchemaTableName.schemaTableName(SNOWFLAKE_TEST_SCHEMA, TpchTable.NATION.getTableName()), viewDefinition, true)) + assertThatThrownBy(() -> catalog.createView(SESSION, SchemaTableName.schemaTableName(SNOWFLAKE_TEST_SCHEMA, TpchTable.NATION.getTableName()), viewDefinition, SaveMode.FAIL)) .hasMessageContaining("Views are not supported for the Snowflake Iceberg catalog"); } diff --git a/plugin/trino-lakehouse/src/main/java/io/trino/plugin/lakehouse/LakehouseMetadata.java b/plugin/trino-lakehouse/src/main/java/io/trino/plugin/lakehouse/LakehouseMetadata.java index dea2a8b15829..4e665c947ee1 100644 --- a/plugin/trino-lakehouse/src/main/java/io/trino/plugin/lakehouse/LakehouseMetadata.java +++ b/plugin/trino-lakehouse/src/main/java/io/trino/plugin/lakehouse/LakehouseMetadata.java @@ -636,12 +636,19 @@ public void finishMerge(ConnectorSession session, ConnectorMergeTableHandle merg forHandle(mergeTableHandle).finishMerge(session, mergeTableHandle, sourceTableHandles, fragments, computedStatistics); } + @Deprecated @Override public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) { hiveMetadata.createView(session, viewName, definition, viewProperties, replace); } + @Override + public void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) + { + hiveMetadata.createView(session, viewName, definition, viewProperties, saveMode); + } + @Override public void renameView(ConnectorSession session, SchemaTableName source, SchemaTableName target) { diff --git a/plugin/trino-memory/src/main/java/io/trino/plugin/memory/MemoryMetadata.java b/plugin/trino-memory/src/main/java/io/trino/plugin/memory/MemoryMetadata.java index f3efbec7246e..f619cd782de3 100644 --- a/plugin/trino-memory/src/main/java/io/trino/plugin/memory/MemoryMetadata.java +++ b/plugin/trino-memory/src/main/java/io/trino/plugin/memory/MemoryMetadata.java @@ -528,15 +528,22 @@ public synchronized void dropNotNullConstraint(ConnectorSession session, Connect } @Override + @Deprecated public synchronized void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, boolean replace) + { + createView(session, viewName, definition, viewProperties, replace ? SaveMode.REPLACE : SaveMode.FAIL); + } + + @Override + public synchronized void createView(ConnectorSession session, SchemaTableName viewName, ConnectorViewDefinition definition, Map viewProperties, SaveMode saveMode) { checkArgument(viewProperties.isEmpty(), "This connector does not support creating views with properties"); checkSchemaExists(viewName.getSchemaName()); - if (tableIds.containsKey(viewName) && !replace) { + if (tableIds.containsKey(viewName) && saveMode != REPLACE) { throw new TrinoException(ALREADY_EXISTS, "View already exists: " + viewName); } - if (replace) { + if (saveMode == REPLACE) { views.put(viewName, definition); } else if (views.putIfAbsent(viewName, definition) != null) { diff --git a/plugin/trino-memory/src/test/java/io/trino/plugin/memory/TestMemoryMetadata.java b/plugin/trino-memory/src/test/java/io/trino/plugin/memory/TestMemoryMetadata.java index 69142ec1a88f..8e743d8c54bc 100644 --- a/plugin/trino-memory/src/test/java/io/trino/plugin/memory/TestMemoryMetadata.java +++ b/plugin/trino-memory/src/test/java/io/trino/plugin/memory/TestMemoryMetadata.java @@ -177,12 +177,12 @@ public void testCreateViewWithoutReplace() MemoryMetadata metadata = createMetadata(); metadata.createSchema(SESSION, "test", ImmutableMap.of(), new TrinoPrincipal(USER, SESSION.getUser())); try { - metadata.createView(SESSION, test, testingViewDefinition("test"), ImmutableMap.of(), false); + metadata.createView(SESSION, test, testingViewDefinition("test"), ImmutableMap.of(), SaveMode.FAIL); } catch (Exception e) { fail("should have succeeded"); } - assertThatThrownBy(() -> metadata.createView(SESSION, test, testingViewDefinition("test"), ImmutableMap.of(), false)) + assertThatThrownBy(() -> metadata.createView(SESSION, test, testingViewDefinition("test"), ImmutableMap.of(), SaveMode.FAIL)) .isInstanceOf(TrinoException.class) .hasMessageMatching("View already exists: test\\.test_view"); } @@ -191,11 +191,10 @@ public void testCreateViewWithoutReplace() public void testCreateViewWithReplace() { SchemaTableName test = new SchemaTableName("test", "test_view"); - MemoryMetadata metadata = createMetadata(); metadata.createSchema(SESSION, "test", ImmutableMap.of(), new TrinoPrincipal(USER, SESSION.getUser())); - metadata.createView(SESSION, test, testingViewDefinition("aaa"), ImmutableMap.of(), true); - metadata.createView(SESSION, test, testingViewDefinition("bbb"), ImmutableMap.of(), true); + metadata.createView(SESSION, test, testingViewDefinition("aaa"), ImmutableMap.of(), SaveMode.REPLACE); + metadata.createView(SESSION, test, testingViewDefinition("bbb"), ImmutableMap.of(), SaveMode.REPLACE); assertThat(metadata.getView(SESSION, test)) .map(ConnectorViewDefinition::getOriginalSql) @@ -210,7 +209,7 @@ public void testCreatedViewShouldBeListedAsTable() MemoryMetadata metadata = createMetadata(); metadata.createSchema(SESSION, schemaName, ImmutableMap.of(), new TrinoPrincipal(USER, SESSION.getUser())); - metadata.createView(SESSION, viewName, testingViewDefinition("aaa"), ImmutableMap.of(), true); + metadata.createView(SESSION, viewName, testingViewDefinition("aaa"), ImmutableMap.of(), SaveMode.REPLACE); assertThat(metadata.listTables(SESSION, Optional.of(schemaName))) .contains(viewName); @@ -228,8 +227,8 @@ public void testViews() metadata.createSchema(SESSION, "test", ImmutableMap.of(), new TrinoPrincipal(USER, SESSION.getUser())); // create views - metadata.createView(SESSION, test1, testingViewDefinition("test1"), ImmutableMap.of(), false); - metadata.createView(SESSION, test2, testingViewDefinition("test2"), ImmutableMap.of(), false); + metadata.createView(SESSION, test1, testingViewDefinition("test1"), ImmutableMap.of(), SaveMode.FAIL); + metadata.createView(SESSION, test2, testingViewDefinition("test2"), ImmutableMap.of(), SaveMode.FAIL); // verify listing List list = metadata.listViews(SESSION, Optional.of("test")); @@ -299,13 +298,13 @@ public void testCreateTableAndViewInNotExistSchema() assertThat(metadata.getTableHandle(SESSION, table1, Optional.empty(), Optional.empty())).isNull(); SchemaTableName view2 = new SchemaTableName("test2", "test_schema_view2"); - assertTrinoExceptionThrownBy(() -> metadata.createView(SESSION, view2, testingViewDefinition("aaa"), ImmutableMap.of(), false)) + assertTrinoExceptionThrownBy(() -> metadata.createView(SESSION, view2, testingViewDefinition("aaa"), ImmutableMap.of(), SaveMode.FAIL)) .hasErrorCode(NOT_FOUND) .hasMessage("Schema test2 not found"); assertThat(metadata.getTableHandle(SESSION, view2, Optional.empty(), Optional.empty())).isNull(); SchemaTableName view3 = new SchemaTableName("test3", "test_schema_view3"); - assertTrinoExceptionThrownBy(() -> metadata.createView(SESSION, view3, testingViewDefinition("bbb"), ImmutableMap.of(), true)) + assertTrinoExceptionThrownBy(() -> metadata.createView(SESSION, view3, testingViewDefinition("bbb"), ImmutableMap.of(), SaveMode.REPLACE)) .hasErrorCode(NOT_FOUND) .hasMessage("Schema test3 not found"); assertThat(metadata.getTableHandle(SESSION, view3, Optional.empty(), Optional.empty())).isNull(); 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 026c0f1eecc1..4476ab84ae4b 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 @@ -1004,6 +1004,8 @@ public void testView() assertUpdate("CREATE VIEW " + testView + " AS SELECT 123 x"); assertThat(computeActual("SHOW TABLES").getOnlyColumnAsSet()) .contains(testView); + assertUpdate("CREATE VIEW IF NOT EXISTS " + testView + " AS SELECT 456 x"); + assertThat(query("SELECT * FROM " + testView)).matches("VALUES 123"); assertUpdate("CREATE OR REPLACE VIEW " + testView + " AS " + query); assertUpdate("CREATE VIEW " + testViewWithComment + " COMMENT 'orders' AS SELECT 123 x"); @@ -1224,6 +1226,29 @@ public void testCreateViewSchemaNotFound() } } + @Test + public void testCreateViewIfNotExist() + { + skipTestUnless(hasBehavior(SUPPORTS_CREATE_VIEW)); + String viewName = "test_create_view_if_not_exists_" + randomNameSuffix(); + String otherViewName = "test_create_view_if_not_exists_" + randomNameSuffix(); + try { + assertUpdate("CREATE VIEW " + viewName + " AS SELECT 1 AS c1"); + assertQuery("SELECT * FROM " + viewName, "SELECT 1"); + assertUpdate("CREATE VIEW IF NOT EXISTS " + viewName + " AS SELECT 1 AS c1"); + assertQuery("SELECT * FROM " + viewName, "SELECT 1"); + assertUpdate("DROP VIEW " + viewName); + + assertUpdate("CREATE VIEW IF NOT EXISTS " + otherViewName + " AS SELECT 1 AS c1"); + assertQuery("SELECT * FROM " + otherViewName, "SELECT 1"); + assertUpdate("DROP VIEW " + otherViewName); + } + finally { + assertUpdate("DROP VIEW IF EXISTS " + viewName); + assertUpdate("DROP VIEW IF EXISTS " + otherViewName); + } + } + @Test public void testViewCaseSensitivity() {