From 7a24249a05df4aa84588d13dd77b9fd488bdd2a8 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Fri, 31 Oct 2025 08:17:52 -0400 Subject: [PATCH 1/4] Add MaterializedViewScanNode --- .../presto/sql/analyzer/Analysis.java | 65 ++++++ .../sql/analyzer/StatementAnalyzer.java | 59 ++++- .../presto/sql/planner/PlanOptimizers.java | 8 + .../presto/sql/planner/RelationPlanner.java | 53 +++++ .../rule/MaterializedViewRewrite.java | 84 +++++++ .../presto/sql/planner/plan/Patterns.java | 6 + .../sanity/ValidateDependenciesChecker.java | 42 ++++ .../rule/TestMaterializedViewRewrite.java | 213 ++++++++++++++++++ .../iterative/rule/test/PlanBuilder.java | 21 ++ .../TestMemoryMaterializedViewPlanner.java | 83 +++++++ .../presto/spi/analyzer/MetadataResolver.java | 9 + .../spi/plan/MaterializedViewScanNode.java | 180 +++++++++++++++ .../facebook/presto/spi/plan/PlanVisitor.java | 5 + 13 files changed, 817 insertions(+), 11 deletions(-) create mode 100644 presto-main-base/src/main/java/com/facebook/presto/sql/planner/iterative/rule/MaterializedViewRewrite.java create mode 100644 presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java create mode 100644 presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java create mode 100644 presto-spi/src/main/java/com/facebook/presto/spi/plan/MaterializedViewScanNode.java diff --git a/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java b/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java index 21e27f1b548f7..a676d8ea82768 100644 --- a/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java +++ b/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java @@ -21,6 +21,7 @@ import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ColumnMetadata; import com.facebook.presto.spi.ConnectorId; +import com.facebook.presto.spi.MaterializedViewDefinition; import com.facebook.presto.spi.TableHandle; import com.facebook.presto.spi.analyzer.AccessControlInfo; import com.facebook.presto.spi.analyzer.AccessControlInfoForTable; @@ -203,6 +204,8 @@ public class Analysis private final Map materializedViews = new LinkedHashMap<>(); + private final Map, MaterializedViewInfo> materializedViewInfoMap = new LinkedHashMap<>(); + private Optional expandedQuery = Optional.empty(); // Keeps track of the subquery we are visiting, so we have access to base query information when processing materialized view status @@ -817,6 +820,19 @@ public boolean hasTableRegisteredForMaterializedView(Table view, Table table) return tablesForMaterializedView.containsEntry(NodeRef.of(view), table); } + public void setMaterializedViewInfo(Table table, MaterializedViewInfo materializedViewInfo) + { + requireNonNull(table, "table is null"); + requireNonNull(materializedViewInfo, "materializedViewInfo is null"); + materializedViewInfoMap.put(NodeRef.of(table), materializedViewInfo); + } + + public Optional getMaterializedViewInfo(Table table) + { + requireNonNull(table, "table is null"); + return Optional.ofNullable(materializedViewInfoMap.get(NodeRef.of(table))); + } + public void setSampleRatio(SampledRelation relation, double ratio) { sampleRatios.put(NodeRef.of(relation), ratio); @@ -1211,6 +1227,55 @@ public Query getQuery() } } + @Immutable + public static final class MaterializedViewInfo + { + private final QualifiedObjectName materializedViewName; + private final QualifiedName storageTableName; + private final Table dataTable; + private final Query viewQuery; + private final MaterializedViewDefinition materializedViewDefinition; + + public MaterializedViewInfo( + QualifiedObjectName materializedViewName, + QualifiedName storageTableName, + Table dataTable, + Query viewQuery, + MaterializedViewDefinition materializedViewDefinition) + { + this.materializedViewName = requireNonNull(materializedViewName, "materializedViewName is null"); + this.storageTableName = requireNonNull(storageTableName, "storageTableName is null"); + this.dataTable = requireNonNull(dataTable, "dataTable is null"); + this.viewQuery = requireNonNull(viewQuery, "viewQuery is null"); + this.materializedViewDefinition = requireNonNull(materializedViewDefinition, "materializedViewDefinition is null"); + } + + public QualifiedObjectName getMaterializedViewName() + { + return materializedViewName; + } + + public QualifiedName getStorageTableName() + { + return storageTableName; + } + + public Table getDataTable() + { + return dataTable; + } + + public Query getViewQuery() + { + return viewQuery; + } + + public MaterializedViewDefinition getMaterializedViewDefinition() + { + return materializedViewDefinition; + } + } + public static final class JoinUsingAnalysis { private final List leftJoinFields; diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java index ca6790b7f4012..5254999d99cfe 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java @@ -2303,21 +2303,58 @@ private Scope processMaterializedView( analysis.getAccessControlReferences().addMaterializedViewDefinitionReference(materializedViewName, materializedViewDefinition); analysis.registerMaterializedViewForAnalysis(materializedViewName, materializedView, materializedViewDefinition.getOriginalSql()); - String newSql = getMaterializedViewSQL(materializedView, materializedViewName, materializedViewDefinition, scope); - Query query = (Query) sqlParser.createStatement(newSql, createParsingOptions(session, warningCollector)); - analysis.registerNamedQuery(materializedView, query, true); + if (isLegacyMaterializedViews(session)) { + // Legacy SQL stitching approach: create UNION query with base tables + String newSql = getMaterializedViewSQL(materializedView, materializedViewName, materializedViewDefinition, scope); - Scope queryScope = process(query, scope); - RelationType relationType = queryScope.getRelationType().withAlias(materializedViewName.getObjectName(), null); - analysis.unregisterMaterializedViewForAnalysis(materializedView); + Query query = (Query) sqlParser.createStatement(newSql, createParsingOptions(session, warningCollector)); + analysis.registerNamedQuery(materializedView, query, true); - Scope accessControlScope = Scope.builder() - .withRelationType(RelationId.anonymous(), relationType) - .build(); - analyzeFiltersAndMasks(materializedView, materializedViewName, accessControlScope, relationType.getAllFields()); + Scope queryScope = process(query, scope); + RelationType relationType = queryScope.getRelationType().withAlias(materializedViewName.getObjectName(), null); + analysis.unregisterMaterializedViewForAnalysis(materializedView); + + Scope accessControlScope = Scope.builder() + .withRelationType(RelationId.anonymous(), relationType) + .build(); + analyzeFiltersAndMasks(materializedView, materializedViewName, accessControlScope, relationType.getAllFields()); - return createAndAssignScope(materializedView, scope, relationType); + return createAndAssignScope(materializedView, scope, relationType); + } + else { + Query viewQuery = (Query) sqlParser.createStatement( + materializedViewDefinition.getOriginalSql(), + createParsingOptions(session, warningCollector)); + + QualifiedName storageTableName = QualifiedName.of( + materializedViewName.getCatalogName(), + materializedViewName.getSchemaName(), + materializedViewDefinition.getTable()); + Table dataTable = new Table(storageTableName); + + Analysis.MaterializedViewInfo mvInfo = new Analysis.MaterializedViewInfo( + materializedViewName, + storageTableName, + dataTable, + viewQuery, + materializedViewDefinition); + analysis.setMaterializedViewInfo(materializedView, mvInfo); + + // Process the view query to analyze base tables for access control + process(viewQuery, scope); + + Scope queryScope = process(dataTable, scope); + RelationType relationType = queryScope.getRelationType().withOnlyVisibleFields().withAlias(materializedViewName.getObjectName(), null); + analysis.unregisterMaterializedViewForAnalysis(materializedView); + + Scope accessControlScope = Scope.builder() + .withRelationType(RelationId.anonymous(), relationType) + .build(); + analyzeFiltersAndMasks(materializedView, materializedViewName, accessControlScope, relationType.getAllFields()); + + return createAndAssignScope(materializedView, scope, relationType); + } } private String getMaterializedViewSQL( diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java index 2d7c8be053645..f414ecab03398 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java @@ -55,6 +55,7 @@ import com.facebook.presto.sql.planner.iterative.rule.InlineSqlFunctions; import com.facebook.presto.sql.planner.iterative.rule.LeftJoinNullFilterToSemiJoin; import com.facebook.presto.sql.planner.iterative.rule.LeftJoinWithArrayContainsToEquiJoinCondition; +import com.facebook.presto.sql.planner.iterative.rule.MaterializedViewRewrite; import com.facebook.presto.sql.planner.iterative.rule.MergeDuplicateAggregation; import com.facebook.presto.sql.planner.iterative.rule.MergeFilters; import com.facebook.presto.sql.planner.iterative.rule.MergeLimitWithDistinct; @@ -314,6 +315,13 @@ public PlanOptimizers( builder.add(new LogicalCteOptimizer(metadata)); + builder.add(new IterativeOptimizer( + metadata, + ruleStats, + statsCalculator, + estimatedExchangesCostCalculator, + ImmutableSet.of(new MaterializedViewRewrite(metadata)))); + IterativeOptimizer inlineProjections = new IterativeOptimizer( metadata, ruleStats, diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java index 0bd4e47fc67b8..10aa1419b2645 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.common.QualifiedObjectName; import com.facebook.presto.common.predicate.TupleDomain; import com.facebook.presto.common.type.ArrayType; import com.facebook.presto.common.type.MapType; @@ -35,6 +36,7 @@ import com.facebook.presto.spi.plan.FilterNode; import com.facebook.presto.spi.plan.IntersectNode; import com.facebook.presto.spi.plan.JoinNode; +import com.facebook.presto.spi.plan.MaterializedViewScanNode; import com.facebook.presto.spi.plan.PlanNode; import com.facebook.presto.spi.plan.PlanNodeIdAllocator; import com.facebook.presto.spi.plan.ProjectNode; @@ -182,6 +184,11 @@ public RelationPlan process(Node node, @Nullable SqlPlannerContext context) @Override protected RelationPlan visitTable(Table node, SqlPlannerContext context) { + Optional materializedViewInfo = analysis.getMaterializedViewInfo(node); + if (materializedViewInfo.isPresent()) { + return planMaterializedView(node, materializedViewInfo.get(), context); + } + NamedQuery namedQuery = analysis.getNamedQuery(node); Scope scope = analysis.getScope(node); @@ -304,6 +311,52 @@ private RelationPlan addColumnMasks(Table table, RelationPlan plan, SqlPlannerCo return new RelationPlan(planBuilder.getRoot(), plan.getScope(), newMappings.build()); } + private RelationPlan planMaterializedView(Table node, Analysis.MaterializedViewInfo mvInfo, SqlPlannerContext context) + { + RelationPlan dataTablePlan = process(mvInfo.getDataTable(), context); + RelationPlan viewQueryPlan = process(mvInfo.getViewQuery(), context); + + Scope scope = analysis.getScope(node); + + QualifiedObjectName materializedViewName = mvInfo.getMaterializedViewName(); + + RelationType dataTableDescriptor = dataTablePlan.getDescriptor(); + List dataTableVariables = dataTablePlan.getFieldMappings(); + List viewQueryVariables = viewQueryPlan.getFieldMappings(); + + ImmutableList.Builder outputVariablesBuilder = ImmutableList.builder(); + ImmutableMap.Builder dataTableMappingsBuilder = ImmutableMap.builder(); + ImmutableMap.Builder viewQueryMappingsBuilder = ImmutableMap.builder(); + + for (Field field : dataTableDescriptor.getVisibleFields()) { + int fieldIndex = dataTableDescriptor.indexOf(field); + VariableReferenceExpression dataTableVar = dataTableVariables.get(fieldIndex); + VariableReferenceExpression viewQueryVar = viewQueryVariables.get(fieldIndex); + + VariableReferenceExpression outputVar = variableAllocator.newVariable(dataTableVar); + outputVariablesBuilder.add(outputVar); + + dataTableMappingsBuilder.put(outputVar, dataTableVar); + viewQueryMappingsBuilder.put(outputVar, viewQueryVar); + } + + List outputVariables = outputVariablesBuilder.build(); + Map dataTableMappings = dataTableMappingsBuilder.build(); + Map viewQueryMappings = viewQueryMappingsBuilder.build(); + + MaterializedViewScanNode mvScanNode = new MaterializedViewScanNode( + getSourceLocation(node.getLocation()), + idAllocator.getNextId(), + dataTablePlan.getRoot(), + viewQueryPlan.getRoot(), + materializedViewName, + dataTableMappings, + viewQueryMappings, + outputVariables); + + return new RelationPlan(mvScanNode, scope, outputVariables); + } + @Override protected RelationPlan visitTableFunctionInvocation(TableFunctionInvocation node, SqlPlannerContext context) { diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/iterative/rule/MaterializedViewRewrite.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/iterative/rule/MaterializedViewRewrite.java new file mode 100644 index 0000000000000..39c3265cc226b --- /dev/null +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/iterative/rule/MaterializedViewRewrite.java @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.planner.iterative.rule; + +import com.facebook.presto.Session; +import com.facebook.presto.matching.Captures; +import com.facebook.presto.matching.Pattern; +import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.spi.MaterializedViewStatus; +import com.facebook.presto.spi.analyzer.MetadataResolver; +import com.facebook.presto.spi.plan.Assignments; +import com.facebook.presto.spi.plan.MaterializedViewScanNode; +import com.facebook.presto.spi.plan.PlanNode; +import com.facebook.presto.spi.plan.ProjectNode; +import com.facebook.presto.spi.relation.VariableReferenceExpression; +import com.facebook.presto.sql.planner.iterative.Rule; + +import java.util.Map; + +import static com.facebook.presto.SystemSessionProperties.isLegacyMaterializedViews; +import static com.facebook.presto.SystemSessionProperties.isMaterializedViewDataConsistencyEnabled; +import static com.facebook.presto.spi.plan.ProjectNode.Locality.LOCAL; +import static com.facebook.presto.sql.planner.plan.Patterns.materializedViewScan; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +public class MaterializedViewRewrite + implements Rule +{ + private final Metadata metadata; + + public MaterializedViewRewrite(Metadata metadata) + { + this.metadata = requireNonNull(metadata, "metadata is null"); + } + + @Override + public Pattern getPattern() + { + return materializedViewScan(); + } + + @Override + public Result apply(MaterializedViewScanNode node, Captures captures, Context context) + { + Session session = context.getSession(); + checkState(!isLegacyMaterializedViews(session), "Materialized view rewrite rule should not fire when legacy materialized views are enabled"); + checkState(isMaterializedViewDataConsistencyEnabled(session), "Materialized view rewrite rule should not fire when materialized view data consistency is disabled"); + + MetadataResolver metadataResolver = metadata.getMetadataResolver(session); + + MaterializedViewStatus status = metadataResolver.getMaterializedViewStatus(node.getMaterializedViewName()); + + boolean useDataTable = status.isFullyMaterialized(); + PlanNode chosenPlan = useDataTable ? node.getDataTablePlan() : node.getViewQueryPlan(); + Map chosenMappings = + useDataTable ? node.getDataTableMappings() : node.getViewQueryMappings(); + + Assignments.Builder assignments = Assignments.builder(); + for (VariableReferenceExpression outputVariable : node.getOutputVariables()) { + VariableReferenceExpression sourceVariable = chosenMappings.get(outputVariable); + requireNonNull(sourceVariable, "No mapping found for output variable: " + outputVariable); + assignments.put(outputVariable, sourceVariable); + } + + return Result.ofPlanNode(new ProjectNode( + node.getSourceLocation(), + context.getIdAllocator().getNextId(), + chosenPlan, + assignments.build(), + LOCAL)); + } +} diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java index f1a00c4b1a128..2e3c9d26c924a 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java @@ -27,6 +27,7 @@ import com.facebook.presto.spi.plan.JoinType; import com.facebook.presto.spi.plan.LimitNode; import com.facebook.presto.spi.plan.MarkDistinctNode; +import com.facebook.presto.spi.plan.MaterializedViewScanNode; import com.facebook.presto.spi.plan.OutputNode; import com.facebook.presto.spi.plan.PlanNode; import com.facebook.presto.spi.plan.ProjectNode; @@ -134,6 +135,11 @@ public static Pattern markDistinct() return typeOf(MarkDistinctNode.class); } + public static Pattern materializedViewScan() + { + return typeOf(MaterializedViewScanNode.class); + } + public static Pattern output() { return typeOf(OutputNode.class); diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java index 7bb6f516b9171..0221661f4b5de 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java @@ -32,6 +32,7 @@ import com.facebook.presto.spi.plan.JoinNode; import com.facebook.presto.spi.plan.LimitNode; import com.facebook.presto.spi.plan.MarkDistinctNode; +import com.facebook.presto.spi.plan.MaterializedViewScanNode; import com.facebook.presto.spi.plan.MergeJoinNode; import com.facebook.presto.spi.plan.MetadataDeleteNode; import com.facebook.presto.spi.plan.OutputNode; @@ -367,6 +368,47 @@ public Void visitDistinctLimit(DistinctLimitNode node, Set boundVariables) + { + PlanNode dataTablePlan = node.getSources().get(0); + PlanNode viewQueryPlan = node.getSources().get(1); + + dataTablePlan.accept(this, boundVariables); + viewQueryPlan.accept(this, boundVariables); + + Set dataTableOutputs = ImmutableSet.copyOf(dataTablePlan.getOutputVariables()); + Set viewQueryOutputs = ImmutableSet.copyOf(viewQueryPlan.getOutputVariables()); + + for (VariableReferenceExpression outputVariable : node.getOutputVariables()) { + VariableReferenceExpression dataTableVariable = node.getDataTableMappings().get(outputVariable); + checkArgument( + dataTableVariable != null, + "Output variable %s has no mapping in dataTableMappings", + outputVariable); + checkArgument( + dataTableOutputs.contains(dataTableVariable), + "Data table mapping variable %s for output %s not in data table plan output (%s)", + dataTableVariable, + outputVariable, + dataTableOutputs); + + VariableReferenceExpression viewQueryVariable = node.getViewQueryMappings().get(outputVariable); + checkArgument( + viewQueryVariable != null, + "Output variable %s has no mapping in viewQueryMappings", + outputVariable); + checkArgument( + viewQueryOutputs.contains(viewQueryVariable), + "View query mapping variable %s for output %s not in view query plan output (%s)", + viewQueryVariable, + outputVariable, + viewQueryOutputs); + } + + return null; + } + @Override public Void visitJoin(JoinNode node, Set boundVariables) { diff --git a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java new file mode 100644 index 0000000000000..e254c34c7d75c --- /dev/null +++ b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java @@ -0,0 +1,213 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.planner.iterative.rule; + +import com.facebook.presto.Session; +import com.facebook.presto.common.QualifiedObjectName; +import com.facebook.presto.common.predicate.TupleDomain; +import com.facebook.presto.metadata.AbstractMockMetadata; +import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.spi.ColumnMetadata; +import com.facebook.presto.spi.MaterializedViewDefinition; +import com.facebook.presto.spi.MaterializedViewStatus; +import com.facebook.presto.spi.TableHandle; +import com.facebook.presto.spi.analyzer.MetadataResolver; +import com.facebook.presto.spi.analyzer.ViewDefinition; +import com.facebook.presto.spi.relation.VariableReferenceExpression; +import com.facebook.presto.sql.planner.iterative.rule.test.BaseRuleTest; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static com.facebook.presto.common.type.BigintType.BIGINT; +import static com.facebook.presto.spi.MaterializedViewStatus.MaterializedViewState.FULLY_MATERIALIZED; +import static com.facebook.presto.spi.MaterializedViewStatus.MaterializedViewState.PARTIALLY_MATERIALIZED; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.expression; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.project; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.values; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; + +public class TestMaterializedViewRewrite + extends BaseRuleTest +{ + @Test + public void testUseFreshDataWhenFullyMaterialized() + { + QualifiedObjectName materializedViewName = QualifiedObjectName.valueOf("catalog.schema.mv"); + + Metadata metadata = new TestingMetadataWithMaterializedViewStatus(true); + + tester().assertThat(new MaterializedViewRewrite(metadata)) + .on(planBuilder -> { + VariableReferenceExpression outputA = planBuilder.variable("a", BIGINT); + VariableReferenceExpression dataTableA = planBuilder.variable("data_table_a", BIGINT); + VariableReferenceExpression viewQueryA = planBuilder.variable("view_query_a", BIGINT); + + return planBuilder.materializedViewScan( + materializedViewName, + planBuilder.values(dataTableA), + planBuilder.values(viewQueryA), + ImmutableMap.of(outputA, dataTableA), + ImmutableMap.of(outputA, viewQueryA), + outputA); + }) + .withSession(testSessionBuilder().setSystemProperty("legacy_materialized_views", "false").build()) + .matches( + project( + ImmutableMap.of("a", expression("data_table_a")), + values("data_table_a"))); + } + + @Test + public void testUseViewQueryWhenNotFullyMaterialized() + { + QualifiedObjectName materializedViewName = QualifiedObjectName.valueOf("catalog.schema.mv"); + + Metadata metadata = new TestingMetadataWithMaterializedViewStatus(false); + + tester().assertThat(new MaterializedViewRewrite(metadata)) + .on(planBuilder -> { + VariableReferenceExpression outputA = planBuilder.variable("a", BIGINT); + VariableReferenceExpression dataTableA = planBuilder.variable("data_table_a", BIGINT); + VariableReferenceExpression viewQueryA = planBuilder.variable("view_query_a", BIGINT); + + return planBuilder.materializedViewScan( + materializedViewName, + planBuilder.values(dataTableA), + planBuilder.values(viewQueryA), + ImmutableMap.of(outputA, dataTableA), + ImmutableMap.of(outputA, viewQueryA), + outputA); + }) + .withSession(testSessionBuilder().setSystemProperty("legacy_materialized_views", "false").build()) + .matches( + project( + ImmutableMap.of("a", expression("view_query_a")), + values("view_query_a"))); + } + + @Test + public void testMultipleOutputVariables() + { + QualifiedObjectName materializedViewName = QualifiedObjectName.valueOf("catalog.schema.mv"); + + Metadata metadata = new TestingMetadataWithMaterializedViewStatus(true); + + tester().assertThat(new MaterializedViewRewrite(metadata)) + .on(planBuilder -> { + VariableReferenceExpression outputA = planBuilder.variable("a", BIGINT); + VariableReferenceExpression outputB = planBuilder.variable("b", BIGINT); + VariableReferenceExpression dataTableA = planBuilder.variable("data_table_a", BIGINT); + VariableReferenceExpression dataTableB = planBuilder.variable("data_table_b", BIGINT); + VariableReferenceExpression viewQueryA = planBuilder.variable("view_query_a", BIGINT); + VariableReferenceExpression viewQueryB = planBuilder.variable("view_query_b", BIGINT); + + return planBuilder.materializedViewScan( + materializedViewName, + planBuilder.values(dataTableA, dataTableB), + planBuilder.values(viewQueryA, viewQueryB), + ImmutableMap.of(outputA, dataTableA, outputB, dataTableB), + ImmutableMap.of(outputA, viewQueryA, outputB, viewQueryB), + outputA, outputB); + }) + .withSession(testSessionBuilder().setSystemProperty("legacy_materialized_views", "false").build()) + .matches( + project( + ImmutableMap.of( + "a", expression("data_table_a"), + "b", expression("data_table_b")), + values("data_table_a", "data_table_b"))); + } + + private static class TestingMetadataWithMaterializedViewStatus + extends AbstractMockMetadata + { + private final boolean isFullyMaterialized; + + public TestingMetadataWithMaterializedViewStatus(boolean isFullyMaterialized) + { + this.isFullyMaterialized = isFullyMaterialized; + } + + @Override + public MetadataResolver getMetadataResolver(Session session) + { + return new MaterializedViewTestingMetadataResolver(super.getMetadataResolver(session), isFullyMaterialized); + } + } + + private static class MaterializedViewTestingMetadataResolver + implements MetadataResolver + { + private final MetadataResolver delegate; + private boolean isFullyMaterialized; + + protected MaterializedViewTestingMetadataResolver(MetadataResolver delegate, boolean isFullyMaterialized) + { + this.delegate = delegate; + this.isFullyMaterialized = isFullyMaterialized; + } + + @Override + public boolean catalogExists(String catalogName) + { + return delegate.catalogExists(catalogName); + } + + @Override + public boolean schemaExists(com.facebook.presto.common.CatalogSchemaName schemaName) + { + return delegate.schemaExists(schemaName); + } + + @Override + public Optional getTableHandle(QualifiedObjectName tableName) + { + return delegate.getTableHandle(tableName); + } + + @Override + public List getColumns(TableHandle tableHandle) + { + return delegate.getColumns(tableHandle); + } + + @Override + public Map getColumnHandles(TableHandle tableHandle) + { + return delegate.getColumnHandles(tableHandle); + } + + @Override + public Optional getView(QualifiedObjectName viewName) + { + return delegate.getView(viewName); + } + + @Override + public Optional getMaterializedView(QualifiedObjectName viewName) + { + return delegate.getMaterializedView(viewName); + } + + @Override + public MaterializedViewStatus getMaterializedViewStatus(QualifiedObjectName materializedViewName, TupleDomain baseQueryDomain) + { + return new MaterializedViewStatus(isFullyMaterialized ? FULLY_MATERIALIZED : PARTIALLY_MATERIALIZED); + } + } +} diff --git a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java index 9482554a3dcfb..6582111703f9d 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java +++ b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.iterative.rule.test; import com.facebook.presto.Session; +import com.facebook.presto.common.QualifiedObjectName; import com.facebook.presto.common.block.SortOrder; import com.facebook.presto.common.function.OperatorType; import com.facebook.presto.common.predicate.TupleDomain; @@ -46,6 +47,7 @@ import com.facebook.presto.spi.plan.JoinType; import com.facebook.presto.spi.plan.LimitNode; import com.facebook.presto.spi.plan.MarkDistinctNode; +import com.facebook.presto.spi.plan.MaterializedViewScanNode; import com.facebook.presto.spi.plan.Ordering; import com.facebook.presto.spi.plan.OrderingScheme; import com.facebook.presto.spi.plan.OutputNode; @@ -1088,4 +1090,23 @@ public GroupIdNode groupId(List> groupingSets, aggregationArguments, groupIdSymbol); } + + public MaterializedViewScanNode materializedViewScan( + QualifiedObjectName materializedViewName, + PlanNode dataTablePlan, + PlanNode viewQueryPlan, + Map dataTableMappings, + Map viewQueryMappings, + VariableReferenceExpression... outputVariables) + { + return new MaterializedViewScanNode( + Optional.empty(), + idAllocator.getNextId(), + dataTablePlan, + viewQueryPlan, + materializedViewName, + dataTableMappings, + viewQueryMappings, + ImmutableList.copyOf(outputVariables)); + } } diff --git a/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java b/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java new file mode 100644 index 0000000000000..cf3b091badd9f --- /dev/null +++ b/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java @@ -0,0 +1,83 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.memory; + +import com.facebook.presto.Session; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.tests.AbstractTestQueryFramework; +import com.facebook.presto.tests.DistributedQueryRunner; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.anyTree; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.tableScan; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; + +/** + * Plan-level tests for materialized views in the Memory connector. + * Tests verify plan structure with legacy_materialized_views=false. + */ +@Test(singleThreaded = true) +public class TestMemoryMaterializedViewPlanner + extends AbstractTestQueryFramework +{ + @Override + protected QueryRunner createQueryRunner() + throws Exception + { + Session session = testSessionBuilder() + .setCatalog("memory") + .setSchema("default") + .setSystemProperty("legacy_materialized_views", "false") + .build(); + + DistributedQueryRunner queryRunner = DistributedQueryRunner.builder(session) + .setNodeCount(4) + .build(); + + queryRunner.installPlugin(new MemoryPlugin()); + queryRunner.createCatalog("memory", "memory", ImmutableMap.of()); + + return queryRunner; + } + + @Test + public void testMaterializedViewNotRefreshed() + { + assertUpdate("CREATE TABLE base_table (id BIGINT, name VARCHAR, value BIGINT)"); + assertUpdate("INSERT INTO base_table VALUES (1, 'Alice', 100), (2, 'Bob', 200)", 2); + assertUpdate("CREATE MATERIALIZED VIEW simple_mv AS SELECT id, name, value FROM base_table"); + + assertPlan(getSession(), "SELECT * FROM simple_mv", + anyTree(tableScan("base_table"))); + + assertUpdate("DROP MATERIALIZED VIEW simple_mv"); + assertUpdate("DROP TABLE base_table"); + } + + @Test + public void testMaterializedViewRefreshed() + { + assertUpdate("CREATE TABLE base_table (id BIGINT, value BIGINT)"); + assertUpdate("INSERT INTO base_table VALUES (1, 100), (2, 200)", 2); + assertUpdate("CREATE MATERIALIZED VIEW mv AS SELECT id, value FROM base_table"); + assertUpdate("REFRESH MATERIALIZED VIEW mv", 2); + + assertPlan("SELECT * FROM mv", + anyTree(tableScan("mv"))); + + assertUpdate("DROP MATERIALIZED VIEW mv"); + assertUpdate("DROP TABLE base_table"); + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/analyzer/MetadataResolver.java b/presto-spi/src/main/java/com/facebook/presto/spi/analyzer/MetadataResolver.java index 768b67485ae3a..7c6f4edc5ecf0 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/analyzer/MetadataResolver.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/analyzer/MetadataResolver.java @@ -116,4 +116,13 @@ default MaterializedViewStatus getMaterializedViewStatus(QualifiedObjectName mat { throw new UnsupportedOperationException("getMaterializedViewStatus is not supported"); } + + /** + * Get the materialized view status to inform the engine how much data has been materialized in the view + * @param materializedViewName materialized view name + */ + default MaterializedViewStatus getMaterializedViewStatus(QualifiedObjectName materializedViewName) + { + return getMaterializedViewStatus(materializedViewName, TupleDomain.all()); + } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/plan/MaterializedViewScanNode.java b/presto-spi/src/main/java/com/facebook/presto/spi/plan/MaterializedViewScanNode.java new file mode 100644 index 0000000000000..f72c7a8ae1737 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/plan/MaterializedViewScanNode.java @@ -0,0 +1,180 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.plan; + +import com.facebook.presto.common.QualifiedObjectName; +import com.facebook.presto.spi.SourceLocation; +import com.facebook.presto.spi.relation.VariableReferenceExpression; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; + +public final class MaterializedViewScanNode + extends PlanNode +{ + private final PlanNode dataTablePlan; + private final PlanNode viewQueryPlan; + private final QualifiedObjectName materializedViewName; + private final Map dataTableMappings; + private final Map viewQueryMappings; + private final List outputVariables; + + @JsonCreator + public MaterializedViewScanNode( + Optional sourceLocation, + @JsonProperty("id") PlanNodeId id, + @JsonProperty("dataTablePlan") PlanNode dataTablePlan, + @JsonProperty("viewQueryPlan") PlanNode viewQueryPlan, + @JsonProperty("materializedViewName") QualifiedObjectName materializedViewName, + @JsonProperty("dataTableMappings") Map dataTableMappings, + @JsonProperty("viewQueryMappings") Map viewQueryMappings, + @JsonProperty("outputVariables") List outputVariables) + { + this(sourceLocation, id, Optional.empty(), dataTablePlan, viewQueryPlan, materializedViewName, dataTableMappings, viewQueryMappings, outputVariables); + } + + public MaterializedViewScanNode( + Optional sourceLocation, + PlanNodeId id, + Optional statsEquivalentPlanNode, + PlanNode dataTablePlan, + PlanNode viewQueryPlan, + QualifiedObjectName materializedViewName, + Map dataTableMappings, + Map viewQueryMappings, + List outputVariables) + { + super(sourceLocation, id, statsEquivalentPlanNode); + this.dataTablePlan = requireNonNull(dataTablePlan, "dataTablePlan is null"); + this.viewQueryPlan = requireNonNull(viewQueryPlan, "viewQueryPlan is null"); + this.materializedViewName = requireNonNull(materializedViewName, "materializedViewName is null"); + this.dataTableMappings = unmodifiableMap(new HashMap<>(requireNonNull(dataTableMappings, "dataTableMappings is null"))); + this.viewQueryMappings = unmodifiableMap(new HashMap<>(requireNonNull(viewQueryMappings, "viewQueryMappings is null"))); + this.outputVariables = unmodifiableList(new ArrayList<>(requireNonNull(outputVariables, "outputVariables is null"))); + } + + @JsonProperty("dataTablePlan") + public PlanNode getDataTablePlan() + { + return dataTablePlan; + } + + @JsonProperty("viewQueryPlan") + public PlanNode getViewQueryPlan() + { + return viewQueryPlan; + } + + @JsonProperty("materializedViewName") + public QualifiedObjectName getMaterializedViewName() + { + return materializedViewName; + } + + @JsonProperty("dataTableMappings") + public Map getDataTableMappings() + { + return dataTableMappings; + } + + @JsonProperty("viewQueryMappings") + public Map getViewQueryMappings() + { + return viewQueryMappings; + } + + @Override + @JsonProperty("outputVariables") + public List getOutputVariables() + { + return outputVariables; + } + + @Override + public List getSources() + { + List sources = new ArrayList<>(2); + sources.add(dataTablePlan); + sources.add(viewQueryPlan); + return unmodifiableList(sources); + } + + public List sourceOutputLayout(int sourceIndex) + { + if (sourceIndex == 0) { + // Data table plan - use dataTableMappings + return outputVariables.stream() + .map(dataTableMappings::get) + .collect(toList()); + } + else if (sourceIndex == 1) { + // View query plan - use viewQueryMappings + return outputVariables.stream() + .map(viewQueryMappings::get) + .collect(toList()); + } + else { + throw new IllegalArgumentException("MaterializedViewScanNode only has 2 sources (index 0 and 1), but got index " + sourceIndex); + } + } + + @Override + public PlanNode replaceChildren(List newChildren) + { + if (newChildren.size() != 2) { + throw new IllegalArgumentException("Expected exactly 2 children but got " + newChildren.size()); + } + return new MaterializedViewScanNode( + getSourceLocation(), + getId(), + getStatsEquivalentPlanNode(), + newChildren.get(0), + newChildren.get(1), + materializedViewName, + dataTableMappings, + viewQueryMappings, + outputVariables); + } + + @Override + public R accept(PlanVisitor visitor, C context) + { + return visitor.visitMaterializedViewScan(this, context); + } + + @Override + public PlanNode assignStatsEquivalentPlanNode(Optional statsEquivalentPlanNode) + { + return new MaterializedViewScanNode( + getSourceLocation(), + getId(), + statsEquivalentPlanNode, + dataTablePlan, + viewQueryPlan, + materializedViewName, + dataTableMappings, + viewQueryMappings, + outputVariables); + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/plan/PlanVisitor.java b/presto-spi/src/main/java/com/facebook/presto/spi/plan/PlanVisitor.java index ddbaa9c349c80..2bf40c8e525ce 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/plan/PlanVisitor.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/plan/PlanVisitor.java @@ -164,4 +164,9 @@ public R visitMetadataDelete(MetadataDeleteNode node, C context) { return visitPlan(node, context); } + + public R visitMaterializedViewScan(MaterializedViewScanNode node, C context) + { + return visitPlan(node, context); + } } From 0db3d6e9122f7b2e0d22b4634890a6c0a42691cd Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Fri, 31 Oct 2025 11:08:17 -0400 Subject: [PATCH 2/4] Code review comments --- .../presto/sql/planner/RelationPlanner.java | 15 +++++++++++---- .../optimizations/ApplyConnectorOptimization.java | 2 ++ .../memory/TestMemoryMaterializedViewPlanner.java | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java index 10aa1419b2645..b83e646b0d95d 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java @@ -311,19 +311,26 @@ private RelationPlan addColumnMasks(Table table, RelationPlan plan, SqlPlannerCo return new RelationPlan(planBuilder.getRoot(), plan.getScope(), newMappings.build()); } - private RelationPlan planMaterializedView(Table node, Analysis.MaterializedViewInfo mvInfo, SqlPlannerContext context) + private RelationPlan planMaterializedView(Table node, Analysis.MaterializedViewInfo materializedViewInfo, SqlPlannerContext context) { - RelationPlan dataTablePlan = process(mvInfo.getDataTable(), context); - RelationPlan viewQueryPlan = process(mvInfo.getViewQuery(), context); + RelationPlan dataTablePlan = process(materializedViewInfo.getDataTable(), context); + RelationPlan viewQueryPlan = process(materializedViewInfo.getViewQuery(), context); Scope scope = analysis.getScope(node); - QualifiedObjectName materializedViewName = mvInfo.getMaterializedViewName(); + QualifiedObjectName materializedViewName = materializedViewInfo.getMaterializedViewName(); RelationType dataTableDescriptor = dataTablePlan.getDescriptor(); List dataTableVariables = dataTablePlan.getFieldMappings(); List viewQueryVariables = viewQueryPlan.getFieldMappings(); + checkArgument( + dataTableVariables.size() == viewQueryVariables.size(), + "Materialized view %s has mismatched field counts: data table has %s fields but view query has %s fields", + materializedViewName, + dataTableVariables.size(), + viewQueryVariables.size()); + ImmutableList.Builder outputVariablesBuilder = ImmutableList.builder(); ImmutableMap.Builder dataTableMappingsBuilder = ImmutableMap.builder(); ImmutableMap.Builder viewQueryMappingsBuilder = ImmutableMap.builder(); diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/ApplyConnectorOptimization.java b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/ApplyConnectorOptimization.java index 4c87bf1d1e40f..8b55d3e9330e8 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/ApplyConnectorOptimization.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/planner/optimizations/ApplyConnectorOptimization.java @@ -34,6 +34,7 @@ import com.facebook.presto.spi.plan.JoinNode; import com.facebook.presto.spi.plan.LimitNode; import com.facebook.presto.spi.plan.MarkDistinctNode; +import com.facebook.presto.spi.plan.MaterializedViewScanNode; import com.facebook.presto.spi.plan.PlanNode; import com.facebook.presto.spi.plan.PlanNodeIdAllocator; import com.facebook.presto.spi.plan.ProjectNode; @@ -89,6 +90,7 @@ public class ApplyConnectorOptimization ProjectNode.class, AggregationNode.class, MarkDistinctNode.class, + MaterializedViewScanNode.class, UnionNode.class, IntersectNode.class, ExceptNode.class, diff --git a/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java b/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java index cf3b091badd9f..3f818bedf6f2f 100644 --- a/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java +++ b/presto-memory/src/test/java/com/facebook/presto/plugin/memory/TestMemoryMaterializedViewPlanner.java @@ -80,4 +80,18 @@ public void testMaterializedViewRefreshed() assertUpdate("DROP MATERIALIZED VIEW mv"); assertUpdate("DROP TABLE base_table"); } + + @Test + public void testQueryDroppedMaterializedView() + { + assertUpdate("CREATE TABLE base_table (id BIGINT, value BIGINT)"); + assertUpdate("INSERT INTO base_table VALUES (1, 100), (2, 200)", 2); + assertUpdate("CREATE MATERIALIZED VIEW dropped_mv AS SELECT id, value FROM base_table"); + + assertUpdate("DROP MATERIALIZED VIEW dropped_mv"); + + assertQueryFails("SELECT * FROM dropped_mv", ".*Table memory\\.default\\.dropped_mv does not exist.*"); + + assertUpdate("DROP TABLE base_table"); + } } From 97d2603aaade4c7f62484ace364dff86cdf79b03 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 3 Nov 2025 08:32:04 -0500 Subject: [PATCH 3/4] Fix import --- .../planner/iterative/rule/TestMaterializedViewRewrite.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java index e254c34c7d75c..bb7f539b383d1 100644 --- a/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java +++ b/presto-main-base/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestMaterializedViewRewrite.java @@ -18,6 +18,7 @@ import com.facebook.presto.common.predicate.TupleDomain; import com.facebook.presto.metadata.AbstractMockMetadata; import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ColumnMetadata; import com.facebook.presto.spi.MaterializedViewDefinition; import com.facebook.presto.spi.MaterializedViewStatus; @@ -187,7 +188,7 @@ public List getColumns(TableHandle tableHandle) } @Override - public Map getColumnHandles(TableHandle tableHandle) + public Map getColumnHandles(TableHandle tableHandle) { return delegate.getColumnHandles(tableHandle); } From f5bb0381a6e8c6a28710159e4706b0a2e4d7d8f5 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 3 Nov 2025 16:45:29 -0500 Subject: [PATCH 4/4] Code review feedback -- remove unused storageTableName --- .../java/com/facebook/presto/sql/analyzer/Analysis.java | 8 -------- .../facebook/presto/sql/analyzer/StatementAnalyzer.java | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java b/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java index a676d8ea82768..3a074b4ad6186 100644 --- a/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java +++ b/presto-analyzer/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java @@ -1231,20 +1231,17 @@ public Query getQuery() public static final class MaterializedViewInfo { private final QualifiedObjectName materializedViewName; - private final QualifiedName storageTableName; private final Table dataTable; private final Query viewQuery; private final MaterializedViewDefinition materializedViewDefinition; public MaterializedViewInfo( QualifiedObjectName materializedViewName, - QualifiedName storageTableName, Table dataTable, Query viewQuery, MaterializedViewDefinition materializedViewDefinition) { this.materializedViewName = requireNonNull(materializedViewName, "materializedViewName is null"); - this.storageTableName = requireNonNull(storageTableName, "storageTableName is null"); this.dataTable = requireNonNull(dataTable, "dataTable is null"); this.viewQuery = requireNonNull(viewQuery, "viewQuery is null"); this.materializedViewDefinition = requireNonNull(materializedViewDefinition, "materializedViewDefinition is null"); @@ -1255,11 +1252,6 @@ public QualifiedObjectName getMaterializedViewName() return materializedViewName; } - public QualifiedName getStorageTableName() - { - return storageTableName; - } - public Table getDataTable() { return dataTable; diff --git a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java index 5254999d99cfe..30ca6602f7b04 100644 --- a/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java +++ b/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java @@ -2327,15 +2327,14 @@ private Scope processMaterializedView( materializedViewDefinition.getOriginalSql(), createParsingOptions(session, warningCollector)); - QualifiedName storageTableName = QualifiedName.of( + QualifiedName dataTableName = QualifiedName.of( materializedViewName.getCatalogName(), materializedViewName.getSchemaName(), materializedViewDefinition.getTable()); - Table dataTable = new Table(storageTableName); + Table dataTable = new Table(dataTableName); Analysis.MaterializedViewInfo mvInfo = new Analysis.MaterializedViewInfo( materializedViewName, - storageTableName, dataTable, viewQuery, materializedViewDefinition);