This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.2 by this push:
new ecbc5feab9d branch-4.2: [fix](mtmv) CREATE MATERIALIZED VIEW IF NOT
EXISTS skips existing sync MV #69370 (#69531)
ecbc5feab9d is described below
commit ecbc5feab9d3518305cab8f4e90e12a5024f391d
Author: yujun <[email protected]>
AuthorDate: Sat Oct 24 10:53:36 2026 +0800
branch-4.2: [fix](mtmv) CREATE MATERIALIZED VIEW IF NOT EXISTS skips
existing sync MV #69370 (#69531)
cherry-pick: #69370
---
.../doris/alter/MaterializedViewHandler.java | 6 ++
.../doris/nereids/parser/LogicalPlanBuilder.java | 3 +-
.../commands/CreateMaterializedViewCommand.java | 8 ++-
.../doris/nereids/parser/NereidsParserTest.java | 15 +++++
.../test_create_mv_if_not_exists.out | 9 +++
.../test_create_mv_if_not_exists.groovy | 74 ++++++++++++++++++++++
6 files changed, 113 insertions(+), 2 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java
b/fe/fe-core/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java
index 5e3e64bf8c8..ac39e630ce3 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/alter/MaterializedViewHandler.java
@@ -202,6 +202,12 @@ public class MaterializedViewHandler extends AlterHandler {
if (olapTable.existTempPartitions()) {
throw new DdlException("Can not alter table when there are
temp partitions in table");
}
+ if (createMvCommand.isIfNotExists()
+ &&
olapTable.hasMaterializedIndex(createMvCommand.getMVName())) {
+ LOG.info("materialized view[{}] already exists on table[{}],
skip creating it",
+ createMvCommand.getMVName(), olapTable.getName());
+ return;
+ }
// check no duplicate column name in full schema
Set<String> allColumnNames = new
TreeSet<>(String.CASE_INSENSITIVE_ORDER);
for (Column column : olapTable.getFullSchema()) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index e98772cd44f..9fffd1180b3 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -1707,7 +1707,8 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
LogicalPlan logicalPlan = new
UnboundResultSink<>(visitQuery(ctx.query()));
Map<String, String> properties = ctx.propertyClause() != null
? Maps.newHashMap(visitPropertyClause(ctx.propertyClause())) :
Maps.newHashMap();
- return new CreateMaterializedViewCommand(new TableNameInfo(nameParts),
logicalPlan, properties);
+ return new CreateMaterializedViewCommand(ctx.EXISTS() != null, new
TableNameInfo(nameParts),
+ logicalPlan, properties);
}
/**
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateMaterializedViewCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateMaterializedViewCommand.java
index fc1327586e6..8ae49261e6a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateMaterializedViewCommand.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateMaterializedViewCommand.java
@@ -105,6 +105,7 @@ import java.util.stream.Collectors;
*/
public class CreateMaterializedViewCommand extends Command implements
ForwardWithSync {
private static final String SYNC_MV_PLANER_DISABLE_RULES =
"HAVING_TO_FILTER";
+ private final boolean ifNotExists;
private final TableNameInfo name;
private final LogicalPlan logicalPlan;
@@ -116,9 +117,10 @@ public class CreateMaterializedViewCommand extends Command
implements ForwardWit
private KeysType mvKeysType;
private OriginStatement originStatement;
- public CreateMaterializedViewCommand(TableNameInfo name, LogicalPlan
logicalPlan,
+ public CreateMaterializedViewCommand(boolean ifNotExists, TableNameInfo
name, LogicalPlan logicalPlan,
Map<String, String> properties) {
super(PlanType.CREATE_MATERIALIZED_VIEW_COMMAND);
+ this.ifNotExists = ifNotExists;
this.name = name;
this.logicalPlan = logicalPlan;
this.properties = properties;
@@ -141,6 +143,10 @@ public class CreateMaterializedViewCommand extends Command
implements ForwardWit
return StmtType.CREATE;
}
+ public boolean isIfNotExists() {
+ return ifNotExists;
+ }
+
public String getMVName() {
return name.getTbl();
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index 6e16892e4bc..c91d01b2928 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -1550,6 +1550,21 @@ public class NereidsParserTest extends ParserTestBase {
Assertions.assertInstanceOf(CreateMaterializedViewCommand.class,
logicalPlan);
}
+ @Test
+ public void testCreateSyncMvIfNotExists() {
+ NereidsParser parser = new NereidsParser();
+ LogicalPlan logicalPlan = parser.parseSingle(
+ "CREATE MATERIALIZED VIEW IF NOT EXISTS mv1 SELECT k1 FROM
t2");
+ CreateMaterializedViewCommand command = Assertions.assertInstanceOf(
+ CreateMaterializedViewCommand.class, logicalPlan);
+ Assertions.assertTrue(command.isIfNotExists());
+
+ LogicalPlan withoutClause = parser.parseSingle("CREATE MATERIALIZED
VIEW mv1 SELECT k1 FROM t2");
+ CreateMaterializedViewCommand withoutClauseCommand =
Assertions.assertInstanceOf(
+ CreateMaterializedViewCommand.class, withoutClause);
+ Assertions.assertFalse(withoutClauseCommand.isIfNotExists());
+ }
+
@Test
public void testDropTemporaryTable() throws Exception {
NereidsParser parser = new NereidsParser();
diff --git
a/regression-test/data/mv_p0/test_create_mv_if_not_exists/test_create_mv_if_not_exists.out
b/regression-test/data/mv_p0/test_create_mv_if_not_exists/test_create_mv_if_not_exists.out
new file mode 100644
index 00000000000..d732e365bf5
--- /dev/null
+++
b/regression-test/data/mv_p0/test_create_mv_if_not_exists/test_create_mv_if_not_exists.out
@@ -0,0 +1,9 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !select_k1 --
+1 10
+2 20
+
+-- !select_k2 --
+1 10
+2 20
+
diff --git
a/regression-test/suites/mv_p0/test_create_mv_if_not_exists/test_create_mv_if_not_exists.groovy
b/regression-test/suites/mv_p0/test_create_mv_if_not_exists/test_create_mv_if_not_exists.groovy
new file mode 100644
index 00000000000..7fc35aea524
--- /dev/null
+++
b/regression-test/suites/mv_p0/test_create_mv_if_not_exists/test_create_mv_if_not_exists.groovy
@@ -0,0 +1,74 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+suite("test_create_mv_if_not_exists") {
+ sql """ DROP TABLE IF EXISTS t_create_mv_if_not_exists """
+ sql """
+ CREATE TABLE t_create_mv_if_not_exists (
+ k1 INT,
+ k2 INT,
+ v1 BIGINT
+ )
+ DUPLICATE KEY(k1)
+ DISTRIBUTED BY HASH(k1) BUCKETS 1
+ PROPERTIES ("replication_num" = "1")
+ """
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv_create_if_not_exists_k1 ON
t_create_mv_if_not_exists """
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv_create_if_not_exists_k2 ON
t_create_mv_if_not_exists """
+ sql """ INSERT INTO t_create_mv_if_not_exists VALUES (1, 1, 10), (2, 2,
20) """
+
+ def syncMvIndexId = { String tableName, String mvName ->
+ def dbId = sql("SHOW PROC '/dbs'").find { it[1].toString() ==
context.dbName }[0]
+ def tableId = sql("SHOW PROC '/dbs/${dbId}'").find { it[1].toString()
== tableName }[0]
+ def rows = sql("SHOW PROC '/dbs/${dbId}/${tableId}/index_schema'")
+ return rows.find { it[1].toString() == mvName }[0].toString()
+ }
+
+ create_sync_mv(context.dbName, "t_create_mv_if_not_exists",
"mv_create_if_not_exists_k1", """
+ SELECT k1 AS mv_k1, SUM(v1) AS mv_sum_v1
+ FROM t_create_mv_if_not_exists
+ GROUP BY k1""")
+ def k1IndexId = syncMvIndexId("t_create_mv_if_not_exists",
"mv_create_if_not_exists_k1")
+
+ // Both variants of IF NOT EXISTS must succeed and leave the existing MV
untouched.
+ sql """
+ CREATE MATERIALIZED VIEW IF NOT EXISTS mv_create_if_not_exists_k1 AS
+ SELECT k1 AS mv_k1, SUM(v1) AS mv_sum_v1
+ FROM t_create_mv_if_not_exists
+ GROUP BY k1
+ """
+ assertEquals(k1IndexId, syncMvIndexId("t_create_mv_if_not_exists",
"mv_create_if_not_exists_k1"))
+ sql """
+ CREATE MATERIALIZED VIEW IF NOT EXISTS mv_create_if_not_exists_k1 AS
+ SELECT k2 AS mv_k2, SUM(v1) AS mv_sum_v1_2
+ FROM t_create_mv_if_not_exists
+ GROUP BY k2
+ """
+ assertEquals(k1IndexId, syncMvIndexId("t_create_mv_if_not_exists",
"mv_create_if_not_exists_k1"))
+
+ // IF NOT EXISTS must still create the sync MV when the name is free.
+ sql """
+ CREATE MATERIALIZED VIEW IF NOT EXISTS mv_create_if_not_exists_k2 AS
+ SELECT k2 AS mv_k2, SUM(v1) AS mv_sum_v1_2
+ FROM t_create_mv_if_not_exists
+ GROUP BY k2
+ """
+ waitingMVTaskFinishedByMvName(context.dbName, "t_create_mv_if_not_exists",
"mv_create_if_not_exists_k2")
+
+ order_qt_select_k1 "SELECT * FROM t_create_mv_if_not_exists INDEX
mv_create_if_not_exists_k1 ORDER BY 1"
+ order_qt_select_k2 "SELECT * FROM t_create_mv_if_not_exists INDEX
mv_create_if_not_exists_k2 ORDER BY 1"
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]