Skip to content

Commit ba0d88e

Browse files
committed
Add engine support for refreshing view
1 parent 2d66d8e commit ba0d88e

25 files changed

+539
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.trino.execution;
15+
16+
import com.google.common.collect.ImmutableMap;
17+
import com.google.common.util.concurrent.ListenableFuture;
18+
import com.google.inject.Inject;
19+
import io.trino.Session;
20+
import io.trino.execution.warnings.WarningCollector;
21+
import io.trino.metadata.Metadata;
22+
import io.trino.metadata.QualifiedObjectName;
23+
import io.trino.metadata.ViewColumn;
24+
import io.trino.metadata.ViewDefinition;
25+
import io.trino.security.AccessControl;
26+
import io.trino.sql.PlannerContext;
27+
import io.trino.sql.analyzer.Analysis;
28+
import io.trino.sql.analyzer.AnalyzerFactory;
29+
import io.trino.sql.parser.SqlParser;
30+
import io.trino.sql.tree.Expression;
31+
import io.trino.sql.tree.RefreshView;
32+
import io.trino.sql.tree.Statement;
33+
34+
import java.util.List;
35+
import java.util.Optional;
36+
37+
import static com.google.common.base.Preconditions.checkArgument;
38+
import static com.google.common.collect.ImmutableList.toImmutableList;
39+
import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
40+
import static io.trino.metadata.MetadataUtil.createQualifiedObjectName;
41+
import static io.trino.spi.StandardErrorCode.TABLE_NOT_FOUND;
42+
import static io.trino.sql.analyzer.SemanticExceptions.semanticException;
43+
import static java.util.Objects.requireNonNull;
44+
45+
public class RefreshViewTask
46+
implements DataDefinitionTask<RefreshView>
47+
{
48+
private final PlannerContext plannerContext;
49+
private final AccessControl accessControl;
50+
private final SqlParser sqlParser;
51+
private final AnalyzerFactory analyzerFactory;
52+
53+
@Inject
54+
public RefreshViewTask(
55+
PlannerContext plannerContext,
56+
AccessControl accessControl,
57+
SqlParser sqlParser,
58+
AnalyzerFactory analyzerFactory)
59+
{
60+
this.plannerContext = requireNonNull(plannerContext, "plannerContext is null");
61+
this.accessControl = requireNonNull(accessControl, "accessControl is null");
62+
this.sqlParser = requireNonNull(sqlParser, "sqlParser is null");
63+
this.analyzerFactory = requireNonNull(analyzerFactory, "analyzerFactory is null");
64+
}
65+
66+
@Override
67+
public String getName()
68+
{
69+
return "REFRESH VIEW";
70+
}
71+
72+
@Override
73+
public ListenableFuture<Void> execute(
74+
RefreshView refreshView,
75+
QueryStateMachine stateMachine,
76+
List<Expression> parameters,
77+
WarningCollector warningCollector)
78+
{
79+
Metadata metadata = plannerContext.getMetadata();
80+
Session session = stateMachine.getSession();
81+
QualifiedObjectName viewName = createQualifiedObjectName(session, refreshView, refreshView.getName());
82+
if (metadata.isMaterializedView(session, viewName)) {
83+
throw semanticException(
84+
TABLE_NOT_FOUND,
85+
refreshView,
86+
"View '%s' does not exist, but a materialized view with that name exists. Did you mean ALTER MATERIALIZED VIEW %s RENAME TO ...?", viewName, viewName);
87+
}
88+
89+
if (!metadata.isView(session, viewName)) {
90+
if (metadata.getTableHandle(session, viewName).isPresent()) {
91+
throw semanticException(
92+
TABLE_NOT_FOUND,
93+
refreshView,
94+
"View '%s' does not exist, but a table with that name exists. Did you mean ALTER TABLE %s RENAME TO ...?", viewName, viewName);
95+
}
96+
97+
throw semanticException(TABLE_NOT_FOUND, refreshView, "View '%s' does not exist", viewName);
98+
}
99+
100+
accessControl.checkCanRefreshView(session.toSecurityContext(), viewName);
101+
102+
ViewDefinition viewDefinition = metadata.getView(session, viewName)
103+
.orElseThrow(() -> semanticException(TABLE_NOT_FOUND, refreshView, "View '%s' not found", viewName));
104+
105+
Session viewSession = stateMachine.getSession();
106+
if (!viewDefinition.isRunAsInvoker()) {
107+
checkArgument(viewDefinition.getRunAsIdentity().isEmpty(), "View owner detail is missing");
108+
viewSession = viewSession.createViewSession(viewDefinition.getCatalog(), viewDefinition.getSchema(), viewDefinition.getRunAsIdentity().get(), viewDefinition.getPath());
109+
}
110+
111+
Statement viewDefinitionSql = sqlParser.createStatement(viewDefinition.getOriginalSql());
112+
113+
Analysis analysis = analyzerFactory.createAnalyzer(viewSession, parameters, ImmutableMap.of(), stateMachine.getWarningCollector(), stateMachine.getPlanOptimizersStatsCollector())
114+
.analyze(viewDefinitionSql);
115+
116+
List<ViewColumn> columns = analysis.getOutputDescriptor(viewDefinitionSql)
117+
.getVisibleFields().stream()
118+
.map(field -> new ViewColumn(field.getName().get(), field.getType().getTypeId(), Optional.empty()))
119+
.collect(toImmutableList());
120+
121+
ViewDefinition viewDefinitionWithNewColumns = new ViewDefinition(
122+
viewDefinition.getOriginalSql(),
123+
viewDefinition.getCatalog(),
124+
viewDefinition.getSchema(),
125+
columns,
126+
viewDefinition.getComment(),
127+
viewDefinition.getRunAsIdentity(),
128+
viewDefinition.getPath());
129+
130+
metadata.refreshView(session, viewName, viewDefinitionWithNewColumns);
131+
132+
return immediateVoidFuture();
133+
}
134+
}

core/trino-main/src/main/java/io/trino/metadata/Metadata.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@ Optional<ConnectorOutputMetadata> finishRefreshMaterializedView(
510510
*/
511511
void renameView(Session session, QualifiedObjectName existingViewName, QualifiedObjectName newViewName);
512512

513+
/**
514+
* Refreshes the view definition.
515+
*/
516+
void refreshView(Session session, QualifiedObjectName viewName, ViewDefinition definition);
517+
513518
/**
514519
* Drops the specified view.
515520
*/

core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,15 @@ public void renameView(Session session, QualifiedObjectName source, QualifiedObj
16091609
}
16101610
}
16111611

1612+
@Override
1613+
public void refreshView(Session session, QualifiedObjectName viewName, ViewDefinition definition)
1614+
{
1615+
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.catalogName());
1616+
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
1617+
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
1618+
metadata.refreshView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), definition.toConnectorViewDefinition());
1619+
}
1620+
16121621
@Override
16131622
public void dropView(Session session, QualifiedObjectName viewName)
16141623
{

core/trino-main/src/main/java/io/trino/security/AccessControl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@ public interface AccessControl
322322
*/
323323
void checkCanRenameView(SecurityContext context, QualifiedObjectName viewName, QualifiedObjectName newViewName);
324324

325+
/**
326+
* Check if identity is allowed to refresh the specified view.
327+
*
328+
* @throws AccessDeniedException if not allowed
329+
*/
330+
void checkCanRefreshView(SecurityContext context, QualifiedObjectName viewName);
331+
325332
/**
326333
* Check if identity is allowed to drop the specified view.
327334
*

core/trino-main/src/main/java/io/trino/security/AccessControlManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,12 @@ public void checkCanRenameView(SecurityContext securityContext, QualifiedObjectN
797797
catalogAuthorizationCheck(viewName.catalogName(), securityContext, (control, context) -> control.checkCanRenameView(context, viewName.asSchemaTableName(), newViewName.asSchemaTableName()));
798798
}
799799

800+
@Override
801+
public void checkCanRefreshView(SecurityContext context, QualifiedObjectName viewName)
802+
{
803+
//
804+
}
805+
800806
@Override
801807
public void checkCanDropView(SecurityContext securityContext, QualifiedObjectName viewName)
802808
{

core/trino-main/src/main/java/io/trino/security/AllowAllAccessControl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ public void checkCanCreateView(SecurityContext context, QualifiedObjectName view
166166
@Override
167167
public void checkCanRenameView(SecurityContext context, QualifiedObjectName viewName, QualifiedObjectName newViewName) {}
168168

169+
@Override
170+
public void checkCanRefreshView(SecurityContext context, QualifiedObjectName viewName) {}
171+
169172
@Override
170173
public void checkCanDropView(SecurityContext context, QualifiedObjectName viewName) {}
171174

core/trino-main/src/main/java/io/trino/security/DenyAllAccessControl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import static io.trino.spi.security.AccessDeniedException.denyKillQuery;
7272
import static io.trino.spi.security.AccessDeniedException.denyReadSystemInformationAccess;
7373
import static io.trino.spi.security.AccessDeniedException.denyRefreshMaterializedView;
74+
import static io.trino.spi.security.AccessDeniedException.denyRefreshView;
7475
import static io.trino.spi.security.AccessDeniedException.denyRenameColumn;
7576
import static io.trino.spi.security.AccessDeniedException.denyRenameMaterializedView;
7677
import static io.trino.spi.security.AccessDeniedException.denyRenameSchema;
@@ -340,6 +341,12 @@ public void checkCanRenameView(SecurityContext context, QualifiedObjectName view
340341
denyRenameView(viewName.toString(), newViewName.toString());
341342
}
342343

344+
@Override
345+
public void checkCanRefreshView(SecurityContext context, QualifiedObjectName viewName)
346+
{
347+
denyRefreshView(viewName.toString());
348+
}
349+
343350
@Override
344351
public void checkCanDropView(SecurityContext context, QualifiedObjectName viewName)
345352
{

core/trino-main/src/main/java/io/trino/security/ForwardingAccessControl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@ public void checkCanRenameView(SecurityContext context, QualifiedObjectName view
290290
delegate().checkCanRenameView(context, viewName, newViewName);
291291
}
292292

293+
@Override
294+
public void checkCanRefreshView(SecurityContext context, QualifiedObjectName viewName)
295+
{
296+
delegate().checkCanRefreshView(context, viewName);
297+
}
298+
293299
@Override
294300
public void checkCanDropView(SecurityContext context, QualifiedObjectName viewName)
295301
{

core/trino-main/src/main/java/io/trino/security/InjectedConnectorAccessControl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ public void checkCanRenameView(ConnectorSecurityContext context, SchemaTableName
279279
accessControl.checkCanRenameView(securityContext, getQualifiedObjectName(viewName), getQualifiedObjectName(viewName));
280280
}
281281

282+
@Override
283+
public void checkCanRefreshView(ConnectorSecurityContext context, SchemaTableName viewName)
284+
{
285+
checkArgument(context == null, "context must be null");
286+
accessControl.checkCanRefreshView(securityContext, getQualifiedObjectName(viewName));
287+
}
288+
282289
@Override
283290
public void checkCanDropView(ConnectorSecurityContext context, SchemaTableName viewName)
284291
{

core/trino-main/src/main/java/io/trino/server/QueryExecutionFactoryModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import io.trino.execution.GrantTask;
4747
import io.trino.execution.PrepareTask;
4848
import io.trino.execution.QueryExecution.QueryExecutionFactory;
49+
import io.trino.execution.RefreshViewTask;
4950
import io.trino.execution.RenameColumnTask;
5051
import io.trino.execution.RenameMaterializedViewTask;
5152
import io.trino.execution.RenameSchemaTask;
@@ -93,6 +94,7 @@
9394
import io.trino.sql.tree.Grant;
9495
import io.trino.sql.tree.GrantRoles;
9596
import io.trino.sql.tree.Prepare;
97+
import io.trino.sql.tree.RefreshView;
9698
import io.trino.sql.tree.RenameColumn;
9799
import io.trino.sql.tree.RenameMaterializedView;
98100
import io.trino.sql.tree.RenameSchema;
@@ -161,6 +163,7 @@ public void configure(Binder binder)
161163
bindDataDefinitionTask(binder, executionBinder, Grant.class, GrantTask.class);
162164
bindDataDefinitionTask(binder, executionBinder, GrantRoles.class, GrantRolesTask.class);
163165
bindDataDefinitionTask(binder, executionBinder, Prepare.class, PrepareTask.class);
166+
bindDataDefinitionTask(binder, executionBinder, RefreshView.class, RefreshViewTask.class);
164167
bindDataDefinitionTask(binder, executionBinder, RenameColumn.class, RenameColumnTask.class);
165168
bindDataDefinitionTask(binder, executionBinder, RenameMaterializedView.class, RenameMaterializedViewTask.class);
166169
bindDataDefinitionTask(binder, executionBinder, RenameSchema.class, RenameSchemaTask.class);

core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@
214214
import io.trino.sql.tree.QueryPeriod;
215215
import io.trino.sql.tree.QuerySpecification;
216216
import io.trino.sql.tree.RefreshMaterializedView;
217+
import io.trino.sql.tree.RefreshView;
217218
import io.trino.sql.tree.Relation;
218219
import io.trino.sql.tree.RenameColumn;
219220
import io.trino.sql.tree.RenameMaterializedView;
@@ -684,6 +685,12 @@ protected Scope visitInsert(Insert insert, Optional<Scope> scope)
684685
return createAndAssignScope(insert, scope, Field.newUnqualified("rows", BIGINT));
685686
}
686687

688+
@Override
689+
protected Scope visitRefreshView(RefreshView node, Optional<Scope> scope)
690+
{
691+
return createAndAssignScope(node, scope);
692+
}
693+
687694
@Override
688695
protected Scope visitRefreshMaterializedView(RefreshMaterializedView refreshMaterializedView, Optional<Scope> scope)
689696
{

core/trino-main/src/main/java/io/trino/tracing/TracingAccessControl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,15 @@ public void checkCanRenameView(SecurityContext context, QualifiedObjectName view
413413
}
414414
}
415415

416+
@Override
417+
public void checkCanRefreshView(SecurityContext context, QualifiedObjectName viewName)
418+
{
419+
Span span = startSpan("checkCanRefreshView");
420+
try (var _ = scopedSpan(span)) {
421+
delegate.checkCanRefreshView(context, viewName);
422+
}
423+
}
424+
416425
@Override
417426
public void checkCanDropView(SecurityContext context, QualifiedObjectName viewName)
418427
{

core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,15 @@ public void renameView(Session session, QualifiedObjectName existingViewName, Qu
925925
}
926926
}
927927

928+
@Override
929+
public void refreshView(Session session, QualifiedObjectName viewName, ViewDefinition viewDefinition)
930+
{
931+
Span span = startSpan("refreshView", viewName);
932+
try (var _ = scopedSpan(span)) {
933+
delegate.refreshView(session, viewName, viewDefinition);
934+
}
935+
}
936+
928937
@Override
929938
public void dropView(Session session, QualifiedObjectName viewName)
930939
{

core/trino-main/src/main/java/io/trino/util/StatementUtils.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import io.trino.execution.GrantRolesTask;
4242
import io.trino.execution.GrantTask;
4343
import io.trino.execution.PrepareTask;
44+
import io.trino.execution.RefreshViewTask;
4445
import io.trino.execution.RenameColumnTask;
4546
import io.trino.execution.RenameMaterializedViewTask;
4647
import io.trino.execution.RenameSchemaTask;
@@ -99,6 +100,7 @@
99100
import io.trino.sql.tree.Prepare;
100101
import io.trino.sql.tree.Query;
101102
import io.trino.sql.tree.RefreshMaterializedView;
103+
import io.trino.sql.tree.RefreshView;
102104
import io.trino.sql.tree.RenameColumn;
103105
import io.trino.sql.tree.RenameMaterializedView;
104106
import io.trino.sql.tree.RenameSchema;
@@ -218,6 +220,7 @@ private StatementUtils() {}
218220
.add(dataDefinitionStatement(Grant.class, GrantTask.class))
219221
.add(dataDefinitionStatement(GrantRoles.class, GrantRolesTask.class))
220222
.add(dataDefinitionStatement(Prepare.class, PrepareTask.class))
223+
.add(dataDefinitionStatement(RefreshView.class, RefreshViewTask.class))
221224
.add(dataDefinitionStatement(RenameColumn.class, RenameColumnTask.class))
222225
.add(dataDefinitionStatement(RenameMaterializedView.class, RenameMaterializedViewTask.class))
223226
.add(dataDefinitionStatement(RenameSchema.class, RenameSchemaTask.class))

core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import io.trino.spi.function.OperatorType;
5252
import io.trino.spi.resourcegroups.ResourceGroupId;
5353
import io.trino.spi.security.Identity;
54+
import io.trino.spi.security.PrincipalType;
5455
import io.trino.spi.security.TrinoPrincipal;
5556
import io.trino.spi.session.PropertyMetadata;
5657
import io.trino.spi.type.Type;
@@ -143,6 +144,7 @@ MATERIALIZED_VIEW_PROPERTY_1_NAME, longProperty(MATERIALIZED_VIEW_PROPERTY_1_NAM
143144
MATERIALIZED_VIEW_PROPERTY_2_NAME, stringProperty(MATERIALIZED_VIEW_PROPERTY_2_NAME, "property 2", MATERIALIZED_VIEW_PROPERTY_2_DEFAULT_VALUE, false));
144145
materializedViewPropertyManager = new MaterializedViewPropertyManager(CatalogServiceProvider.singleton(TEST_CATALOG_HANDLE, properties));
145146
queryStateMachine = stateMachine(transactionManager, createTestMetadataManager(), new AllowAllAccessControl(), testSession);
147+
metadata.createSchema(testSession, new CatalogSchemaName(TEST_CATALOG_NAME, SCHEMA), ImmutableMap.of(), new TrinoPrincipal(PrincipalType.ROLE, "role"));
146148
}
147149

148150
@AfterEach
@@ -592,6 +594,12 @@ public void renameView(Session session, QualifiedObjectName source, QualifiedObj
592594
views.remove(oldViewName);
593595
}
594596

597+
@Override
598+
public void refreshView(Session session, QualifiedObjectName viewName, ViewDefinition viewDefinition)
599+
{
600+
views.replace(viewName.asSchemaTableName(), viewDefinition);
601+
}
602+
595603
@Override
596604
public void setTableComment(Session session, TableHandle tableHandle, Optional<String> comment)
597605
{

0 commit comments

Comments
 (0)